From f465e838d025472bf344243b0ab25ea4bbd77f42 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Thu, 17 Jul 2025 10:39:30 +0000 Subject: [PATCH 01/11] parser:nested struct decl --- _xtool/internal/parser/parser.go | 51 ++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/_xtool/internal/parser/parser.go b/_xtool/internal/parser/parser.go index 30d3099d..568de3b1 100644 --- a/_xtool/internal/parser/parser.go +++ b/_xtool/internal/parser/parser.go @@ -33,6 +33,9 @@ type Converter struct { index *clang.Index unit *clang.TranslationUnit indent int // for verbose debug + + // For collecting extracted named nested struct declarations + extractedDecls []ast.Decl } var tagMap = map[string]ast.Tag{ @@ -247,6 +250,12 @@ func (ct *Converter) visitTop(cursor, parent clang.Cursor) clang.ChildVisitResul ct.logln("visitTop: ProcessClassDecl END", classDecl.Name.Name) case clang.CursorStructDecl: structDecl := ct.ProcessStructDecl(cursor) + + // Add extracted nested struct declarations first + for _, extractedDecl := range ct.extractedDecls { + ct.file.Decls = append(ct.file.Decls, extractedDecl) + } + ct.file.Decls = append(ct.file.Decls, structDecl) ct.logf("visitTop: ProcessStructDecl END") if structDecl.Name != nil { @@ -256,6 +265,12 @@ func (ct *Converter) visitTop(cursor, parent clang.Cursor) clang.ChildVisitResul } case clang.CursorUnionDecl: unionDecl := ct.ProcessUnionDecl(cursor) + + // Add extracted nested struct declarations first + for _, extractedDecl := range ct.extractedDecls { + ct.file.Decls = append(ct.file.Decls, extractedDecl) + } + ct.file.Decls = append(ct.file.Decls, unionDecl) ct.logf("visitTop: ProcessUnionDecl END") if unionDecl.Name != nil { @@ -759,12 +774,48 @@ func (ct *Converter) ProcessMethods(cursor clang.Cursor) []*ast.FuncDecl { return methods } +// extractNestedStructs extracts named nested struct declarations from a record cursor +func (ct *Converter) extractNestedStructs(cursor clang.Cursor) { + extractedNames := make(map[string]bool) + + clangutils.VisitChildren(cursor, func(child, parent clang.Cursor) clang.ChildVisitResult { + if child.Kind == clang.CursorStructDecl || child.Kind == clang.CursorUnionDecl { + // Check if this is a named nested struct/union + if child.IsAnonymousRecordDecl() == 0 { + childName := clang.GoString(child.String()) + + // Avoid duplicates + if !extractedNames[childName] { + extractedNames[childName] = true + ct.logln("extractNestedStructs: Found named nested struct:", childName) + + // Create a separate TypeDecl for this nested struct + nestedDecl := &ast.TypeDecl{ + Object: ct.CreateObject(child, &ast.Ident{Name: childName}), + Type: ct.ProcessRecordType(child), + } + + // Add to extracted declarations + ct.extractedDecls = append(ct.extractedDecls, nestedDecl) + } + } + } + return clang.ChildVisit_Recurse + }) +} + func (ct *Converter) ProcessRecordDecl(cursor clang.Cursor) *ast.TypeDecl { ct.incIndent() defer ct.decIndent() cursorName, cursorKind := getCursorDesc(cursor) ct.logln("ProcessRecordDecl: CursorName:", cursorName, "CursorKind:", cursorKind) + // Clear extracted decls before processing this record + ct.extractedDecls = ct.extractedDecls[:0] + + // Extract nested struct declarations first + ct.extractNestedStructs(cursor) + decl := &ast.TypeDecl{ Object: ct.CreateObject(cursor, nil), Type: ct.ProcessRecordType(cursor), From 1ed436956c5f3bc3978b1325d01f55eaf6d60f4d Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Thu, 17 Jul 2025 10:51:19 +0000 Subject: [PATCH 02/11] test case --- _xtool/internal/parser/parser_test.go | 17 ++- .../internal/parser/testdata/tt/expect.json | 139 ++++++++++++++++++ _xtool/internal/parser/testdata/tt/temp.h | 9 ++ 3 files changed, 161 insertions(+), 4 deletions(-) create mode 100755 _xtool/internal/parser/testdata/tt/expect.json create mode 100644 _xtool/internal/parser/testdata/tt/temp.h diff --git a/_xtool/internal/parser/parser_test.go b/_xtool/internal/parser/parser_test.go index 3d1536e8..72b5fa6c 100644 --- a/_xtool/internal/parser/parser_test.go +++ b/_xtool/internal/parser/parser_test.go @@ -25,12 +25,21 @@ func TestParser(t *testing.T) { // todo(zzy):use os.ReadDir for _, folder := range cases { t.Run(folder, func(t *testing.T) { - testFrom(t, filepath.Join("testdata", folder), "temp.h", false) + testFrom(t, filepath.Join("testdata", folder), "temp.h", true, false) }) } } -func testFrom(t *testing.T, dir string, filename string, gen bool) { +func TestParserC(t *testing.T) { + cases := []string{"tt"} + for _, folder := range cases { + t.Run(folder, func(t *testing.T) { + testFrom(t, filepath.Join("testdata", folder), "temp.h", false, false) + }) + } +} + +func testFrom(t *testing.T, dir string, filename string, isCpp, gen bool) { var expect string var err error if !gen { @@ -42,7 +51,7 @@ func testFrom(t *testing.T, dir string, filename string, gen bool) { } ast, err := parser.Do(&parser.ConverterConfig{ File: filepath.Join(dir, filename), - IsCpp: true, + IsCpp: false, Args: []string{"-fparse-all-comments"}, }) if err != nil { @@ -67,7 +76,7 @@ func testFrom(t *testing.T, dir string, filename string, gen bool) { } } -func TestNonBuiltinTypes(t *testing.T) { +func TestNonBuiltinTypes_disabled(t *testing.T) { tests := []struct { TypeCode string ExpectTypeStr string diff --git a/_xtool/internal/parser/testdata/tt/expect.json b/_xtool/internal/parser/testdata/tt/expect.json new file mode 100755 index 00000000..fec7f8fb --- /dev/null +++ b/_xtool/internal/parser/testdata/tt/expect.json @@ -0,0 +1,139 @@ +{ + "_Type": "File", + "decls": [ + { + "Doc": null, + "Loc": { + "File": "testdata/tt/temp.h", + "_Type": "Location" + }, + "Name": { + "Name": "inner_struct", + "_Type": "Ident" + }, + "Parent": null, + "Type": { + "Fields": { + "List": [ + { + "Access": 1, + "Comment": null, + "Doc": null, + "IsStatic": false, + "Names": [ + { + "Name": "l", + "_Type": "Ident" + } + ], + "Type": { + "Flags": 4, + "Kind": 6, + "_Type": "BuiltinType" + }, + "_Type": "Field" + }, + { + "Access": 1, + "Comment": null, + "Doc": null, + "IsStatic": false, + "Names": [ + { + "Name": "b", + "_Type": "Ident" + } + ], + "Type": { + "Elt": { + "Flags": 2, + "Kind": 2, + "_Type": "BuiltinType" + }, + "Len": { + "Kind": 0, + "Value": "60", + "_Type": "BasicLit" + }, + "_Type": "ArrayType" + }, + "_Type": "Field" + } + ], + "_Type": "FieldList" + }, + "Methods": null, + "Tag": 0, + "_Type": "RecordType" + }, + "_Type": "TypeDecl" + }, + { + "Doc": null, + "Loc": { + "File": "testdata/tt/temp.h", + "_Type": "Location" + }, + "Name": { + "Name": "struct2", + "_Type": "Ident" + }, + "Parent": null, + "Type": { + "Fields": { + "List": [ + { + "Access": 1, + "Comment": null, + "Doc": null, + "IsStatic": false, + "Names": [ + { + "Name": "b", + "_Type": "Ident" + } + ], + "Type": { + "X": { + "Flags": 2, + "Kind": 2, + "_Type": "BuiltinType" + }, + "_Type": "PointerType" + }, + "_Type": "Field" + }, + { + "Access": 1, + "Comment": null, + "Doc": null, + "IsStatic": false, + "Names": [ + { + "Name": "init", + "_Type": "Ident" + } + ], + "Type": { + "Name": { + "Name": "inner_struct", + "_Type": "Ident" + }, + "Tag": 0, + "_Type": "TagExpr" + }, + "_Type": "Field" + } + ], + "_Type": "FieldList" + }, + "Methods": null, + "Tag": 0, + "_Type": "RecordType" + }, + "_Type": "TypeDecl" + } + ], + "includes": null, + "macros": null +} \ No newline at end of file diff --git a/_xtool/internal/parser/testdata/tt/temp.h b/_xtool/internal/parser/testdata/tt/temp.h new file mode 100644 index 00000000..6fb5cc18 --- /dev/null +++ b/_xtool/internal/parser/testdata/tt/temp.h @@ -0,0 +1,9 @@ +struct struct2 { + char *b; + struct inner_struct { + long l; + char b[60]; + } init; +}; + +struct struct2 struct inner_struct; \ No newline at end of file From e3da5ab9c928385a5949be2500da68b99a42979c Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Thu, 17 Jul 2025 11:18:53 +0000 Subject: [PATCH 03/11] parser:refine anonymouse judge --- _xtool/internal/parser/parser.go | 22 +++++++++++----------- _xtool/internal/parser/parser_test.go | 4 ++-- _xtool/internal/parser/testdata/tt/temp.h | 4 +--- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/_xtool/internal/parser/parser.go b/_xtool/internal/parser/parser.go index 568de3b1..f9a1bc43 100644 --- a/_xtool/internal/parser/parser.go +++ b/_xtool/internal/parser/parser.go @@ -33,7 +33,7 @@ type Converter struct { index *clang.Index unit *clang.TranslationUnit indent int // for verbose debug - + // For collecting extracted named nested struct declarations extractedDecls []ast.Decl } @@ -250,12 +250,12 @@ func (ct *Converter) visitTop(cursor, parent clang.Cursor) clang.ChildVisitResul ct.logln("visitTop: ProcessClassDecl END", classDecl.Name.Name) case clang.CursorStructDecl: structDecl := ct.ProcessStructDecl(cursor) - + // Add extracted nested struct declarations first for _, extractedDecl := range ct.extractedDecls { ct.file.Decls = append(ct.file.Decls, extractedDecl) } - + ct.file.Decls = append(ct.file.Decls, structDecl) ct.logf("visitTop: ProcessStructDecl END") if structDecl.Name != nil { @@ -265,12 +265,12 @@ func (ct *Converter) visitTop(cursor, parent clang.Cursor) clang.ChildVisitResul } case clang.CursorUnionDecl: unionDecl := ct.ProcessUnionDecl(cursor) - + // Add extracted nested struct declarations first for _, extractedDecl := range ct.extractedDecls { ct.file.Decls = append(ct.file.Decls, extractedDecl) } - + ct.file.Decls = append(ct.file.Decls, unionDecl) ct.logf("visitTop: ProcessUnionDecl END") if unionDecl.Name != nil { @@ -777,24 +777,24 @@ func (ct *Converter) ProcessMethods(cursor clang.Cursor) []*ast.FuncDecl { // extractNestedStructs extracts named nested struct declarations from a record cursor func (ct *Converter) extractNestedStructs(cursor clang.Cursor) { extractedNames := make(map[string]bool) - + clangutils.VisitChildren(cursor, func(child, parent clang.Cursor) clang.ChildVisitResult { if child.Kind == clang.CursorStructDecl || child.Kind == clang.CursorUnionDecl { // Check if this is a named nested struct/union - if child.IsAnonymousRecordDecl() == 0 { + if child.IsAnonymous() == 0 { childName := clang.GoString(child.String()) - + // Avoid duplicates if !extractedNames[childName] { extractedNames[childName] = true ct.logln("extractNestedStructs: Found named nested struct:", childName) - + // Create a separate TypeDecl for this nested struct nestedDecl := &ast.TypeDecl{ Object: ct.CreateObject(child, &ast.Ident{Name: childName}), Type: ct.ProcessRecordType(child), } - + // Add to extracted declarations ct.extractedDecls = append(ct.extractedDecls, nestedDecl) } @@ -812,7 +812,7 @@ func (ct *Converter) ProcessRecordDecl(cursor clang.Cursor) *ast.TypeDecl { // Clear extracted decls before processing this record ct.extractedDecls = ct.extractedDecls[:0] - + // Extract nested struct declarations first ct.extractNestedStructs(cursor) diff --git a/_xtool/internal/parser/parser_test.go b/_xtool/internal/parser/parser_test.go index 72b5fa6c..c0718cc2 100644 --- a/_xtool/internal/parser/parser_test.go +++ b/_xtool/internal/parser/parser_test.go @@ -25,7 +25,7 @@ func TestParser(t *testing.T) { // todo(zzy):use os.ReadDir for _, folder := range cases { t.Run(folder, func(t *testing.T) { - testFrom(t, filepath.Join("testdata", folder), "temp.h", true, false) + testFrom(t, filepath.Join("testdata", folder), "temp.h", true, true) }) } } @@ -51,7 +51,7 @@ func testFrom(t *testing.T, dir string, filename string, isCpp, gen bool) { } ast, err := parser.Do(&parser.ConverterConfig{ File: filepath.Join(dir, filename), - IsCpp: false, + IsCpp: isCpp, Args: []string{"-fparse-all-comments"}, }) if err != nil { diff --git a/_xtool/internal/parser/testdata/tt/temp.h b/_xtool/internal/parser/testdata/tt/temp.h index 6fb5cc18..809847e7 100644 --- a/_xtool/internal/parser/testdata/tt/temp.h +++ b/_xtool/internal/parser/testdata/tt/temp.h @@ -4,6 +4,4 @@ struct struct2 { long l; char b[60]; } init; -}; - -struct struct2 struct inner_struct; \ No newline at end of file +}; \ No newline at end of file From 2db7ab9f66616beaa30722f13f4fbfbcfedb3e8f Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Fri, 18 Jul 2025 02:31:59 +0000 Subject: [PATCH 04/11] update case --- .../sqlite3/3.49.1/sqlite3/llcppg.pub | 3 + .../sqlite3/3.49.1/sqlite3/sqlite3.go | 13 +++- .../parser/testdata/forwarddecl1/expect.json | 66 +++++++++++++++++++ .../internal/parser/testdata/tt/expect.json | 8 +-- _xtool/internal/parser/testdata/tt/temp.h | 4 +- .../_testdata/forwarddecl/gogensig.expect | 9 +-- cmd/gogensig/testdata/lua/gogensig.expect | 1 + 7 files changed, 91 insertions(+), 13 deletions(-) diff --git a/_cmptest/testdata/sqlite3/3.49.1/sqlite3/llcppg.pub b/_cmptest/testdata/sqlite3/3.49.1/sqlite3/llcppg.pub index af95480d..b34755ea 100644 --- a/_cmptest/testdata/sqlite3/3.49.1/sqlite3/llcppg.pub +++ b/_cmptest/testdata/sqlite3/3.49.1/sqlite3/llcppg.pub @@ -15,7 +15,10 @@ sqlite3_context Context sqlite3_destructor_type DestructorType sqlite3_file File sqlite3_filename Filename +sqlite3_index_constraint IndexConstraint +sqlite3_index_constraint_usage IndexConstraintUsage sqlite3_index_info IndexInfo +sqlite3_index_orderby IndexOrderby sqlite3_int64 Int64 sqlite3_io_methods IoMethods sqlite3_loadext_entry LoadextEntry diff --git a/_cmptest/testdata/sqlite3/3.49.1/sqlite3/sqlite3.go b/_cmptest/testdata/sqlite3/3.49.1/sqlite3/sqlite3.go index 21e0b3a5..59c9717d 100644 --- a/_cmptest/testdata/sqlite3/3.49.1/sqlite3/sqlite3.go +++ b/_cmptest/testdata/sqlite3/3.49.1/sqlite3/sqlite3.go @@ -5689,15 +5689,22 @@ type Module struct { } type IndexConstraint struct { - Unused [8]uint8 + IColumn c.Int + Op c.Char + Usable c.Char + ITermOffset c.Int } type IndexOrderby struct { - Unused [8]uint8 + IColumn c.Int + Desc c.Char } +/* Outputs */ + type IndexConstraintUsage struct { - Unused [8]uint8 + ArgvIndex c.Int + Omit c.Char } /* diff --git a/_xtool/internal/parser/testdata/forwarddecl1/expect.json b/_xtool/internal/parser/testdata/forwarddecl1/expect.json index 25fff7bc..4ffc5f3f 100755 --- a/_xtool/internal/parser/testdata/forwarddecl1/expect.json +++ b/_xtool/internal/parser/testdata/forwarddecl1/expect.json @@ -1,6 +1,28 @@ { "_Type": "File", "decls": [ + { + "Doc": null, + "Loc": { + "File": "testdata/forwarddecl1/temp.h", + "_Type": "Location" + }, + "Name": { + "Name": "bar", + "_Type": "Ident" + }, + "Parent": null, + "Type": { + "Fields": { + "List": null, + "_Type": "FieldList" + }, + "Methods": null, + "Tag": 0, + "_Type": "RecordType" + }, + "_Type": "TypeDecl" + }, { "Doc": null, "Loc": { @@ -377,6 +399,28 @@ }, "_Type": "TypeDecl" }, + { + "Doc": null, + "Loc": { + "File": "testdata/forwarddecl1/temp.h", + "_Type": "Location" + }, + "Name": { + "Name": "sqlite3_io_methods", + "_Type": "Ident" + }, + "Parent": null, + "Type": { + "Fields": { + "List": null, + "_Type": "FieldList" + }, + "Methods": null, + "Tag": 0, + "_Type": "RecordType" + }, + "_Type": "TypeDecl" + }, { "Doc": null, "Loc": { @@ -661,6 +705,28 @@ }, "_Type": "FuncDecl" }, + { + "Doc": null, + "Loc": { + "File": "testdata/forwarddecl1/temp.h", + "_Type": "Location" + }, + "Name": { + "Name": "CallInfo", + "_Type": "Ident" + }, + "Parent": null, + "Type": { + "Fields": { + "List": null, + "_Type": "FieldList" + }, + "Methods": null, + "Tag": 0, + "_Type": "RecordType" + }, + "_Type": "TypeDecl" + }, { "Doc": null, "Loc": { diff --git a/_xtool/internal/parser/testdata/tt/expect.json b/_xtool/internal/parser/testdata/tt/expect.json index fec7f8fb..f22e4dec 100755 --- a/_xtool/internal/parser/testdata/tt/expect.json +++ b/_xtool/internal/parser/testdata/tt/expect.json @@ -46,8 +46,8 @@ ], "Type": { "Elt": { - "Flags": 2, - "Kind": 2, + "Flags": 0, + "Kind": 6, "_Type": "BuiltinType" }, "Len": { @@ -95,8 +95,8 @@ ], "Type": { "X": { - "Flags": 2, - "Kind": 2, + "Flags": 0, + "Kind": 6, "_Type": "BuiltinType" }, "_Type": "PointerType" diff --git a/_xtool/internal/parser/testdata/tt/temp.h b/_xtool/internal/parser/testdata/tt/temp.h index 809847e7..e74aa979 100644 --- a/_xtool/internal/parser/testdata/tt/temp.h +++ b/_xtool/internal/parser/testdata/tt/temp.h @@ -1,7 +1,7 @@ struct struct2 { - char *b; + int *b; struct inner_struct { long l; - char b[60]; + int b[60]; } init; }; \ No newline at end of file diff --git a/cl/internal/convert/_testdata/forwarddecl/gogensig.expect b/cl/internal/convert/_testdata/forwarddecl/gogensig.expect index ab6678ba..5d68ff3b 100644 --- a/cl/internal/convert/_testdata/forwarddecl/gogensig.expect +++ b/cl/internal/convert/_testdata/forwarddecl/gogensig.expect @@ -22,10 +22,6 @@ type File struct { PMethods *IoMethods } -type IoMethods struct { - XUnfetch c.Pointer -} - ===== temp.go ===== package forwarddecl @@ -40,6 +36,10 @@ type Bar struct { A *Foo } +type IoMethods struct { + XUnfetch c.Pointer +} + type PcachePage struct { PBuf c.Pointer PExtra c.Pointer @@ -130,6 +130,7 @@ type XmlParserCtxt X_xmlParserCtxt type HtmlParserCtxt XmlParserCtxt ===== llcppg.pub ===== +CallInfo Fts5Context Fts5ExtensionApi Fts5PhraseIter diff --git a/cmd/gogensig/testdata/lua/gogensig.expect b/cmd/gogensig/testdata/lua/gogensig.expect index 743b1c50..54803735 100644 --- a/cmd/gogensig/testdata/lua/gogensig.expect +++ b/cmd/gogensig/testdata/lua/gogensig.expect @@ -695,6 +695,7 @@ func Package(L *State) c.Int func Openlibs(L *State) ===== llcppg.pub ===== +CallInfo luaL_Buffer Buffer luaL_Reg Reg luaL_Stream Stream From 575fd7b912de4679c8d2f28e1ad19f61a704d157 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Fri, 18 Jul 2025 07:24:06 +0000 Subject: [PATCH 05/11] parser:avoid fetch forward decl to ast --- _xtool/internal/parser/parser.go | 11 ++-- .../parser/testdata/forwarddecl1/expect.json | 66 ------------------- .../_testdata/forwarddecl/gogensig.expect | 9 ++- cmd/gogensig/testdata/lua/gogensig.expect | 1 - 4 files changed, 9 insertions(+), 78 deletions(-) diff --git a/_xtool/internal/parser/parser.go b/_xtool/internal/parser/parser.go index f9a1bc43..ed6bdb5a 100644 --- a/_xtool/internal/parser/parser.go +++ b/_xtool/internal/parser/parser.go @@ -781,21 +781,20 @@ func (ct *Converter) extractNestedStructs(cursor clang.Cursor) { clangutils.VisitChildren(cursor, func(child, parent clang.Cursor) clang.ChildVisitResult { if child.Kind == clang.CursorStructDecl || child.Kind == clang.CursorUnionDecl { // Check if this is a named nested struct/union - if child.IsAnonymous() == 0 { + typ := ct.ProcessRecordType(child) + // use len(typ.Fields.List) to ensure it has fields not a forward declaration + // but maybe make the forward decl in to AST is also good. + if child.IsAnonymous() == 0 && len(typ.Fields.List) > 0 { childName := clang.GoString(child.String()) - - // Avoid duplicates if !extractedNames[childName] { extractedNames[childName] = true - ct.logln("extractNestedStructs: Found named nested struct:", childName) + ct.logln("extractNestedStructs: Found named nested struct:", childName, child.Type().Kind) - // Create a separate TypeDecl for this nested struct nestedDecl := &ast.TypeDecl{ Object: ct.CreateObject(child, &ast.Ident{Name: childName}), Type: ct.ProcessRecordType(child), } - // Add to extracted declarations ct.extractedDecls = append(ct.extractedDecls, nestedDecl) } } diff --git a/_xtool/internal/parser/testdata/forwarddecl1/expect.json b/_xtool/internal/parser/testdata/forwarddecl1/expect.json index 4ffc5f3f..25fff7bc 100755 --- a/_xtool/internal/parser/testdata/forwarddecl1/expect.json +++ b/_xtool/internal/parser/testdata/forwarddecl1/expect.json @@ -1,28 +1,6 @@ { "_Type": "File", "decls": [ - { - "Doc": null, - "Loc": { - "File": "testdata/forwarddecl1/temp.h", - "_Type": "Location" - }, - "Name": { - "Name": "bar", - "_Type": "Ident" - }, - "Parent": null, - "Type": { - "Fields": { - "List": null, - "_Type": "FieldList" - }, - "Methods": null, - "Tag": 0, - "_Type": "RecordType" - }, - "_Type": "TypeDecl" - }, { "Doc": null, "Loc": { @@ -399,28 +377,6 @@ }, "_Type": "TypeDecl" }, - { - "Doc": null, - "Loc": { - "File": "testdata/forwarddecl1/temp.h", - "_Type": "Location" - }, - "Name": { - "Name": "sqlite3_io_methods", - "_Type": "Ident" - }, - "Parent": null, - "Type": { - "Fields": { - "List": null, - "_Type": "FieldList" - }, - "Methods": null, - "Tag": 0, - "_Type": "RecordType" - }, - "_Type": "TypeDecl" - }, { "Doc": null, "Loc": { @@ -705,28 +661,6 @@ }, "_Type": "FuncDecl" }, - { - "Doc": null, - "Loc": { - "File": "testdata/forwarddecl1/temp.h", - "_Type": "Location" - }, - "Name": { - "Name": "CallInfo", - "_Type": "Ident" - }, - "Parent": null, - "Type": { - "Fields": { - "List": null, - "_Type": "FieldList" - }, - "Methods": null, - "Tag": 0, - "_Type": "RecordType" - }, - "_Type": "TypeDecl" - }, { "Doc": null, "Loc": { diff --git a/cl/internal/convert/_testdata/forwarddecl/gogensig.expect b/cl/internal/convert/_testdata/forwarddecl/gogensig.expect index 5d68ff3b..ab6678ba 100644 --- a/cl/internal/convert/_testdata/forwarddecl/gogensig.expect +++ b/cl/internal/convert/_testdata/forwarddecl/gogensig.expect @@ -22,6 +22,10 @@ type File struct { PMethods *IoMethods } +type IoMethods struct { + XUnfetch c.Pointer +} + ===== temp.go ===== package forwarddecl @@ -36,10 +40,6 @@ type Bar struct { A *Foo } -type IoMethods struct { - XUnfetch c.Pointer -} - type PcachePage struct { PBuf c.Pointer PExtra c.Pointer @@ -130,7 +130,6 @@ type XmlParserCtxt X_xmlParserCtxt type HtmlParserCtxt XmlParserCtxt ===== llcppg.pub ===== -CallInfo Fts5Context Fts5ExtensionApi Fts5PhraseIter diff --git a/cmd/gogensig/testdata/lua/gogensig.expect b/cmd/gogensig/testdata/lua/gogensig.expect index 54803735..743b1c50 100644 --- a/cmd/gogensig/testdata/lua/gogensig.expect +++ b/cmd/gogensig/testdata/lua/gogensig.expect @@ -695,7 +695,6 @@ func Package(L *State) c.Int func Openlibs(L *State) ===== llcppg.pub ===== -CallInfo luaL_Buffer Buffer luaL_Reg Reg luaL_Stream Stream From 22a2584f0c4ad2bf9445eafdf7708b838364c2e9 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Fri, 18 Jul 2025 08:44:21 +0000 Subject: [PATCH 06/11] verify ast.Node order fetch --- _xtool/internal/parser/parser_test.go | 48 ++++++++++++++++++- .../testdata/named_nested_struct/temp.h | 15 ++++++ _xtool/internal/parser/testdata/tt/temp.h | 2 +- 3 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 _xtool/internal/parser/testdata/named_nested_struct/temp.h diff --git a/_xtool/internal/parser/parser_test.go b/_xtool/internal/parser/parser_test.go index c0718cc2..7e1fb4ef 100644 --- a/_xtool/internal/parser/parser_test.go +++ b/_xtool/internal/parser/parser_test.go @@ -76,7 +76,7 @@ func testFrom(t *testing.T, dir string, filename string, isCpp, gen bool) { } } -func TestNonBuiltinTypes_disabled(t *testing.T) { +func TestNonBuiltinTypes(t *testing.T) { tests := []struct { TypeCode string ExpectTypeStr string @@ -627,3 +627,49 @@ func compareOutput(t *testing.T, expected, actual string) { t.Fatalf("Test failed: expected \n%s \ngot \n%s", expected, actual) } } + +func TestNest(t *testing.T) { + config := &clangutils.Config{ + File: "./testdata/named_nested_struct/temp.h", + Temp: false, + IsCpp: false, + } + + name := make(map[string]bool) + visit(config, func(cursor, parent clang.Cursor) clang.ChildVisitResult { + if cursor.Kind == clang.CursorStructDecl { + if !name[clang.GoString(cursor.String())] { + name[clang.GoString(cursor.String())] = true + file, line, column := clangutils.GetPresumedLocation(cursor.Location()) + fmt.Println("StructDecl Name:", clang.GoString(cursor.String()), file, line, column) + } + } + return clang.ChildVisit_Recurse + }) + + index, unit, err := clangutils.CreateTranslationUnit(config) + if err != nil { + panic(err) + } + defer index.Dispose() + defer unit.Dispose() + + childs := getChild(unit.Cursor()) + expect := []string{"c", "d", "b", "f", "e", "a"} + if !reflect.DeepEqual(expect, childs) { + fmt.Println("Unexpected child order:", childs) + } +} + +func getChild(cursor clang.Cursor) []string { + var children []string + clangutils.VisitChildren(cursor, func(child, parent clang.Cursor) clang.ChildVisitResult { + if child.Kind == clang.CursorStructDecl { + childs := getChild(child) + children = append(children, childs[:]...) + children = append(children, clang.GoString(child.String())) + } + return clang.ChildVisit_Continue + }) + return children +} diff --git a/_xtool/internal/parser/testdata/named_nested_struct/temp.h b/_xtool/internal/parser/testdata/named_nested_struct/temp.h new file mode 100644 index 00000000..c9fc5fe2 --- /dev/null +++ b/_xtool/internal/parser/testdata/named_nested_struct/temp.h @@ -0,0 +1,15 @@ +struct a { + struct b { + struct c { + int a; + } c_field; + struct d { + int b; + } d_field; + } b_field; + struct e { + struct f { + int b; + } f_field; + } e_field; +}; diff --git a/_xtool/internal/parser/testdata/tt/temp.h b/_xtool/internal/parser/testdata/tt/temp.h index e74aa979..b723b2e5 100644 --- a/_xtool/internal/parser/testdata/tt/temp.h +++ b/_xtool/internal/parser/testdata/tt/temp.h @@ -4,4 +4,4 @@ struct struct2 { long l; int b[60]; } init; -}; \ No newline at end of file +}; From 7ef7a02b1acfadee081c5fa4dae4efea88faa496 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Fri, 18 Jul 2025 09:08:56 +0000 Subject: [PATCH 07/11] parser:GetChilds with condition --- _xtool/internal/parser/parser.go | 13 +++++++++++++ _xtool/internal/parser/parser_test.go | 25 +++++++++---------------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/_xtool/internal/parser/parser.go b/_xtool/internal/parser/parser.go index ed6bdb5a..c5c8bb3b 100644 --- a/_xtool/internal/parser/parser.go +++ b/_xtool/internal/parser/parser.go @@ -1009,6 +1009,19 @@ func (ct *Converter) BuildScopingExpr(cursor clang.Cursor) ast.Expr { return buildScopingFromParts(parts) } +func GetChilds(cursor clang.Cursor, collect func(c, p clang.Cursor) bool) []clang.Cursor { + var children []clang.Cursor + clangutils.VisitChildren(cursor, func(child, parent clang.Cursor) clang.ChildVisitResult { + if collect(child, parent) { + childs := GetChilds(child, collect) + children = append(children, childs[:]...) + children = append(children, child) + } + return clang.ChildVisit_Continue + }) + return children +} + func IsExplicitSigned(t clang.Type) bool { return t.Kind == clang.TypeCharS || t.Kind == clang.TypeSChar } diff --git a/_xtool/internal/parser/parser_test.go b/_xtool/internal/parser/parser_test.go index 7e1fb4ef..491a2987 100644 --- a/_xtool/internal/parser/parser_test.go +++ b/_xtool/internal/parser/parser_test.go @@ -654,22 +654,15 @@ func TestNest(t *testing.T) { defer index.Dispose() defer unit.Dispose() - childs := getChild(unit.Cursor()) + childStr := make([]string, 6) + childs := parser.GetChilds(unit.Cursor(), func(child, parent clang.Cursor) bool { + return child.Kind == clang.CursorStructDecl + }) + for i, child := range childs { + childStr[i] = clang.GoString(child.String()) + } expect := []string{"c", "d", "b", "f", "e", "a"} - if !reflect.DeepEqual(expect, childs) { - fmt.Println("Unexpected child order:", childs) + if !reflect.DeepEqual(expect, childStr) { + fmt.Println("Unexpected child order:", childStr) } } - -func getChild(cursor clang.Cursor) []string { - var children []string - clangutils.VisitChildren(cursor, func(child, parent clang.Cursor) clang.ChildVisitResult { - if child.Kind == clang.CursorStructDecl { - childs := getChild(child) - children = append(children, childs[:]...) - children = append(children, clang.GoString(child.String())) - } - return clang.ChildVisit_Continue - }) - return children -} From b726387a623da15080e5f40245442bfc4fe51ef9 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Fri, 18 Jul 2025 09:46:15 +0000 Subject: [PATCH 08/11] preorder child list -> postorder traversal --- _xtool/internal/parser/parser.go | 64 ++-- _xtool/internal/parser/parser_test.go | 10 +- .../testdata/named_nested_struct/expect.json | 304 ++++++++++++++++++ .../internal/parser/testdata/tt/expect.json | 139 -------- _xtool/internal/parser/testdata/tt/temp.h | 7 - 5 files changed, 339 insertions(+), 185 deletions(-) create mode 100755 _xtool/internal/parser/testdata/named_nested_struct/expect.json delete mode 100755 _xtool/internal/parser/testdata/tt/expect.json delete mode 100644 _xtool/internal/parser/testdata/tt/temp.h diff --git a/_xtool/internal/parser/parser.go b/_xtool/internal/parser/parser.go index c5c8bb3b..dd16bf05 100644 --- a/_xtool/internal/parser/parser.go +++ b/_xtool/internal/parser/parser.go @@ -245,39 +245,18 @@ func (ct *Converter) visitTop(cursor, parent clang.Cursor) clang.ChildVisitResul case clang.CursorClassDecl: classDecl := ct.ProcessClassDecl(cursor) + // todo(zzy):class need consider nested struct situation ct.file.Decls = append(ct.file.Decls, classDecl) // class havent anonymous situation ct.logln("visitTop: ProcessClassDecl END", classDecl.Name.Name) case clang.CursorStructDecl: - structDecl := ct.ProcessStructDecl(cursor) - - // Add extracted nested struct declarations first - for _, extractedDecl := range ct.extractedDecls { - ct.file.Decls = append(ct.file.Decls, extractedDecl) - } - - ct.file.Decls = append(ct.file.Decls, structDecl) + decls := ct.ProcessStructDecl(cursor) + ct.file.Decls = append(ct.file.Decls, decls...) ct.logf("visitTop: ProcessStructDecl END") - if structDecl.Name != nil { - ct.logln(structDecl.Name.Name) - } else { - ct.logln("ANONY") - } case clang.CursorUnionDecl: - unionDecl := ct.ProcessUnionDecl(cursor) - - // Add extracted nested struct declarations first - for _, extractedDecl := range ct.extractedDecls { - ct.file.Decls = append(ct.file.Decls, extractedDecl) - } - - ct.file.Decls = append(ct.file.Decls, unionDecl) + decls := ct.ProcessUnionDecl(cursor) + ct.file.Decls = append(ct.file.Decls, decls...) ct.logf("visitTop: ProcessUnionDecl END") - if unionDecl.Name != nil { - ct.logln(unionDecl.Name.Name) - } else { - ct.logln("ANONY") - } case clang.CursorFunctionDecl, clang.CursorCXXMethod, clang.CursorConstructor, clang.CursorDestructor: // Handle functions and class methods (including out-of-class method) // Example: void MyClass::myMethod() { ... } out-of-class method @@ -803,17 +782,32 @@ func (ct *Converter) extractNestedStructs(cursor clang.Cursor) { }) } -func (ct *Converter) ProcessRecordDecl(cursor clang.Cursor) *ast.TypeDecl { +func (ct *Converter) ProcessRecordDecl(cursor clang.Cursor) []ast.Decl { + var decls []ast.Decl ct.incIndent() defer ct.decIndent() cursorName, cursorKind := getCursorDesc(cursor) ct.logln("ProcessRecordDecl: CursorName:", cursorName, "CursorKind:", cursorKind) - // Clear extracted decls before processing this record - ct.extractedDecls = ct.extractedDecls[:0] + childs := GetChilds(cursor, func(child, parent clang.Cursor) bool { + return (child.Kind == clang.CursorStructDecl || child.Kind == clang.CursorUnionDecl) && child.IsAnonymous() == 0 + }) - // Extract nested struct declarations first - ct.extractNestedStructs(cursor) + for _, child := range childs { + + // Check if this is a named nested struct/union + typ := ct.ProcessRecordType(child) + // note(zzy):use len(typ.Fields.List) to ensure it has fields not a forward declaration + // but maybe make the forward decl in to AST is also good. + if child.IsAnonymous() == 0 && len(typ.Fields.List) > 0 { + childName := clang.GoString(child.String()) + ct.logln("Found named nested struct:", childName, child.Type().Kind) + decls = append(decls, &ast.TypeDecl{ + Object: ct.CreateObject(child, &ast.Ident{Name: childName}), + Type: ct.ProcessRecordType(child), + }) + } + } decl := &ast.TypeDecl{ Object: ct.CreateObject(cursor, nil), @@ -828,14 +822,16 @@ func (ct *Converter) ProcessRecordDecl(cursor clang.Cursor) *ast.TypeDecl { ct.logln("ProcessRecordDecl: is anonymous") } - return decl + decls = append(decls, decl) + + return decls } -func (ct *Converter) ProcessStructDecl(cursor clang.Cursor) *ast.TypeDecl { +func (ct *Converter) ProcessStructDecl(cursor clang.Cursor) []ast.Decl { return ct.ProcessRecordDecl(cursor) } -func (ct *Converter) ProcessUnionDecl(cursor clang.Cursor) *ast.TypeDecl { +func (ct *Converter) ProcessUnionDecl(cursor clang.Cursor) []ast.Decl { return ct.ProcessRecordDecl(cursor) } diff --git a/_xtool/internal/parser/parser_test.go b/_xtool/internal/parser/parser_test.go index 491a2987..059fe6c1 100644 --- a/_xtool/internal/parser/parser_test.go +++ b/_xtool/internal/parser/parser_test.go @@ -19,19 +19,19 @@ import ( "github.com/goplus/llgo/xtool/clang/preprocessor" ) -func TestParser(t *testing.T) { +func TestParserCppMode(t *testing.T) { cases := []string{"class", "comment", "enum", "func", "scope", "struct", "typedef", "union", "macro", "forwarddecl1", "forwarddecl2", "include", "typeof"} // https://github.com/goplus/llgo/issues/1114 // todo(zzy):use os.ReadDir for _, folder := range cases { t.Run(folder, func(t *testing.T) { - testFrom(t, filepath.Join("testdata", folder), "temp.h", true, true) + testFrom(t, filepath.Join("testdata", folder), "temp.h", true, false) }) } } -func TestParserC(t *testing.T) { - cases := []string{"tt"} +func TestParserCMode(t *testing.T) { + cases := []string{"named_nested_struct"} for _, folder := range cases { t.Run(folder, func(t *testing.T) { testFrom(t, filepath.Join("testdata", folder), "temp.h", false, false) @@ -628,7 +628,7 @@ func compareOutput(t *testing.T, expected, actual string) { } } -func TestNest(t *testing.T) { +func TestGetChilds(t *testing.T) { config := &clangutils.Config{ File: "./testdata/named_nested_struct/temp.h", Temp: false, diff --git a/_xtool/internal/parser/testdata/named_nested_struct/expect.json b/_xtool/internal/parser/testdata/named_nested_struct/expect.json new file mode 100755 index 00000000..3a01cf5f --- /dev/null +++ b/_xtool/internal/parser/testdata/named_nested_struct/expect.json @@ -0,0 +1,304 @@ +{ + "_Type": "File", + "decls": [ + { + "Doc": null, + "Loc": { + "File": "testdata/named_nested_struct/temp.h", + "_Type": "Location" + }, + "Name": { + "Name": "c", + "_Type": "Ident" + }, + "Parent": null, + "Type": { + "Fields": { + "List": [ + { + "Access": 1, + "Comment": null, + "Doc": null, + "IsStatic": false, + "Names": [ + { + "Name": "a", + "_Type": "Ident" + } + ], + "Type": { + "Flags": 0, + "Kind": 6, + "_Type": "BuiltinType" + }, + "_Type": "Field" + } + ], + "_Type": "FieldList" + }, + "Methods": null, + "Tag": 0, + "_Type": "RecordType" + }, + "_Type": "TypeDecl" + }, + { + "Doc": null, + "Loc": { + "File": "testdata/named_nested_struct/temp.h", + "_Type": "Location" + }, + "Name": { + "Name": "d", + "_Type": "Ident" + }, + "Parent": null, + "Type": { + "Fields": { + "List": [ + { + "Access": 1, + "Comment": null, + "Doc": null, + "IsStatic": false, + "Names": [ + { + "Name": "b", + "_Type": "Ident" + } + ], + "Type": { + "Flags": 0, + "Kind": 6, + "_Type": "BuiltinType" + }, + "_Type": "Field" + } + ], + "_Type": "FieldList" + }, + "Methods": null, + "Tag": 0, + "_Type": "RecordType" + }, + "_Type": "TypeDecl" + }, + { + "Doc": null, + "Loc": { + "File": "testdata/named_nested_struct/temp.h", + "_Type": "Location" + }, + "Name": { + "Name": "b", + "_Type": "Ident" + }, + "Parent": null, + "Type": { + "Fields": { + "List": [ + { + "Access": 1, + "Comment": null, + "Doc": null, + "IsStatic": false, + "Names": [ + { + "Name": "c_field", + "_Type": "Ident" + } + ], + "Type": { + "Name": { + "Name": "c", + "_Type": "Ident" + }, + "Tag": 0, + "_Type": "TagExpr" + }, + "_Type": "Field" + }, + { + "Access": 1, + "Comment": null, + "Doc": null, + "IsStatic": false, + "Names": [ + { + "Name": "d_field", + "_Type": "Ident" + } + ], + "Type": { + "Name": { + "Name": "d", + "_Type": "Ident" + }, + "Tag": 0, + "_Type": "TagExpr" + }, + "_Type": "Field" + } + ], + "_Type": "FieldList" + }, + "Methods": null, + "Tag": 0, + "_Type": "RecordType" + }, + "_Type": "TypeDecl" + }, + { + "Doc": null, + "Loc": { + "File": "testdata/named_nested_struct/temp.h", + "_Type": "Location" + }, + "Name": { + "Name": "f", + "_Type": "Ident" + }, + "Parent": null, + "Type": { + "Fields": { + "List": [ + { + "Access": 1, + "Comment": null, + "Doc": null, + "IsStatic": false, + "Names": [ + { + "Name": "b", + "_Type": "Ident" + } + ], + "Type": { + "Flags": 0, + "Kind": 6, + "_Type": "BuiltinType" + }, + "_Type": "Field" + } + ], + "_Type": "FieldList" + }, + "Methods": null, + "Tag": 0, + "_Type": "RecordType" + }, + "_Type": "TypeDecl" + }, + { + "Doc": null, + "Loc": { + "File": "testdata/named_nested_struct/temp.h", + "_Type": "Location" + }, + "Name": { + "Name": "e", + "_Type": "Ident" + }, + "Parent": null, + "Type": { + "Fields": { + "List": [ + { + "Access": 1, + "Comment": null, + "Doc": null, + "IsStatic": false, + "Names": [ + { + "Name": "f_field", + "_Type": "Ident" + } + ], + "Type": { + "Name": { + "Name": "f", + "_Type": "Ident" + }, + "Tag": 0, + "_Type": "TagExpr" + }, + "_Type": "Field" + } + ], + "_Type": "FieldList" + }, + "Methods": null, + "Tag": 0, + "_Type": "RecordType" + }, + "_Type": "TypeDecl" + }, + { + "Doc": null, + "Loc": { + "File": "testdata/named_nested_struct/temp.h", + "_Type": "Location" + }, + "Name": { + "Name": "a", + "_Type": "Ident" + }, + "Parent": null, + "Type": { + "Fields": { + "List": [ + { + "Access": 1, + "Comment": null, + "Doc": null, + "IsStatic": false, + "Names": [ + { + "Name": "b_field", + "_Type": "Ident" + } + ], + "Type": { + "Name": { + "Name": "b", + "_Type": "Ident" + }, + "Tag": 0, + "_Type": "TagExpr" + }, + "_Type": "Field" + }, + { + "Access": 1, + "Comment": null, + "Doc": null, + "IsStatic": false, + "Names": [ + { + "Name": "e_field", + "_Type": "Ident" + } + ], + "Type": { + "Name": { + "Name": "e", + "_Type": "Ident" + }, + "Tag": 0, + "_Type": "TagExpr" + }, + "_Type": "Field" + } + ], + "_Type": "FieldList" + }, + "Methods": null, + "Tag": 0, + "_Type": "RecordType" + }, + "_Type": "TypeDecl" + } + ], + "includes": null, + "macros": null +} \ No newline at end of file diff --git a/_xtool/internal/parser/testdata/tt/expect.json b/_xtool/internal/parser/testdata/tt/expect.json deleted file mode 100755 index f22e4dec..00000000 --- a/_xtool/internal/parser/testdata/tt/expect.json +++ /dev/null @@ -1,139 +0,0 @@ -{ - "_Type": "File", - "decls": [ - { - "Doc": null, - "Loc": { - "File": "testdata/tt/temp.h", - "_Type": "Location" - }, - "Name": { - "Name": "inner_struct", - "_Type": "Ident" - }, - "Parent": null, - "Type": { - "Fields": { - "List": [ - { - "Access": 1, - "Comment": null, - "Doc": null, - "IsStatic": false, - "Names": [ - { - "Name": "l", - "_Type": "Ident" - } - ], - "Type": { - "Flags": 4, - "Kind": 6, - "_Type": "BuiltinType" - }, - "_Type": "Field" - }, - { - "Access": 1, - "Comment": null, - "Doc": null, - "IsStatic": false, - "Names": [ - { - "Name": "b", - "_Type": "Ident" - } - ], - "Type": { - "Elt": { - "Flags": 0, - "Kind": 6, - "_Type": "BuiltinType" - }, - "Len": { - "Kind": 0, - "Value": "60", - "_Type": "BasicLit" - }, - "_Type": "ArrayType" - }, - "_Type": "Field" - } - ], - "_Type": "FieldList" - }, - "Methods": null, - "Tag": 0, - "_Type": "RecordType" - }, - "_Type": "TypeDecl" - }, - { - "Doc": null, - "Loc": { - "File": "testdata/tt/temp.h", - "_Type": "Location" - }, - "Name": { - "Name": "struct2", - "_Type": "Ident" - }, - "Parent": null, - "Type": { - "Fields": { - "List": [ - { - "Access": 1, - "Comment": null, - "Doc": null, - "IsStatic": false, - "Names": [ - { - "Name": "b", - "_Type": "Ident" - } - ], - "Type": { - "X": { - "Flags": 0, - "Kind": 6, - "_Type": "BuiltinType" - }, - "_Type": "PointerType" - }, - "_Type": "Field" - }, - { - "Access": 1, - "Comment": null, - "Doc": null, - "IsStatic": false, - "Names": [ - { - "Name": "init", - "_Type": "Ident" - } - ], - "Type": { - "Name": { - "Name": "inner_struct", - "_Type": "Ident" - }, - "Tag": 0, - "_Type": "TagExpr" - }, - "_Type": "Field" - } - ], - "_Type": "FieldList" - }, - "Methods": null, - "Tag": 0, - "_Type": "RecordType" - }, - "_Type": "TypeDecl" - } - ], - "includes": null, - "macros": null -} \ No newline at end of file diff --git a/_xtool/internal/parser/testdata/tt/temp.h b/_xtool/internal/parser/testdata/tt/temp.h deleted file mode 100644 index b723b2e5..00000000 --- a/_xtool/internal/parser/testdata/tt/temp.h +++ /dev/null @@ -1,7 +0,0 @@ -struct struct2 { - int *b; - struct inner_struct { - long l; - int b[60]; - } init; -}; From aa47de08c5e4856dcf7cfcecfe5677a893388285 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Fri, 18 Jul 2025 10:07:01 +0000 Subject: [PATCH 09/11] cl:case with nest named struct --- _xtool/internal/parser/parser.go | 36 +-------------- .../convert/_testdata/nested/gogensig.expect | 45 +++++++++++++++++++ .../convert/_testdata/nested/hfile/temp.h | 29 +++++++++++- 3 files changed, 74 insertions(+), 36 deletions(-) diff --git a/_xtool/internal/parser/parser.go b/_xtool/internal/parser/parser.go index dd16bf05..4292f126 100644 --- a/_xtool/internal/parser/parser.go +++ b/_xtool/internal/parser/parser.go @@ -33,9 +33,6 @@ type Converter struct { index *clang.Index unit *clang.TranslationUnit indent int // for verbose debug - - // For collecting extracted named nested struct declarations - extractedDecls []ast.Decl } var tagMap = map[string]ast.Tag{ @@ -753,35 +750,6 @@ func (ct *Converter) ProcessMethods(cursor clang.Cursor) []*ast.FuncDecl { return methods } -// extractNestedStructs extracts named nested struct declarations from a record cursor -func (ct *Converter) extractNestedStructs(cursor clang.Cursor) { - extractedNames := make(map[string]bool) - - clangutils.VisitChildren(cursor, func(child, parent clang.Cursor) clang.ChildVisitResult { - if child.Kind == clang.CursorStructDecl || child.Kind == clang.CursorUnionDecl { - // Check if this is a named nested struct/union - typ := ct.ProcessRecordType(child) - // use len(typ.Fields.List) to ensure it has fields not a forward declaration - // but maybe make the forward decl in to AST is also good. - if child.IsAnonymous() == 0 && len(typ.Fields.List) > 0 { - childName := clang.GoString(child.String()) - if !extractedNames[childName] { - extractedNames[childName] = true - ct.logln("extractNestedStructs: Found named nested struct:", childName, child.Type().Kind) - - nestedDecl := &ast.TypeDecl{ - Object: ct.CreateObject(child, &ast.Ident{Name: childName}), - Type: ct.ProcessRecordType(child), - } - - ct.extractedDecls = append(ct.extractedDecls, nestedDecl) - } - } - } - return clang.ChildVisit_Recurse - }) -} - func (ct *Converter) ProcessRecordDecl(cursor clang.Cursor) []ast.Decl { var decls []ast.Decl ct.incIndent() @@ -794,14 +762,13 @@ func (ct *Converter) ProcessRecordDecl(cursor clang.Cursor) []ast.Decl { }) for _, child := range childs { - // Check if this is a named nested struct/union typ := ct.ProcessRecordType(child) // note(zzy):use len(typ.Fields.List) to ensure it has fields not a forward declaration // but maybe make the forward decl in to AST is also good. if child.IsAnonymous() == 0 && len(typ.Fields.List) > 0 { childName := clang.GoString(child.String()) - ct.logln("Found named nested struct:", childName, child.Type().Kind) + ct.logln("ProcessRecordDecl: Found named nested struct:", childName) decls = append(decls, &ast.TypeDecl{ Object: ct.CreateObject(child, &ast.Ident{Name: childName}), Type: ct.ProcessRecordType(child), @@ -823,7 +790,6 @@ func (ct *Converter) ProcessRecordDecl(cursor clang.Cursor) []ast.Decl { } decls = append(decls, decl) - return decls } diff --git a/cl/internal/convert/_testdata/nested/gogensig.expect b/cl/internal/convert/_testdata/nested/gogensig.expect index f2abfce3..b7736df2 100644 --- a/cl/internal/convert/_testdata/nested/gogensig.expect +++ b/cl/internal/convert/_testdata/nested/gogensig.expect @@ -21,6 +21,16 @@ type Struct1 struct { } } +type InnerStruct struct { + L c.Long +} + +// https://github.com/goplus/llcppg/issues/514 +// named nested struct +type StructWithNested struct { + Init InnerStruct +} + type Struct2 struct { B *c.Char Size c.SizeT @@ -46,8 +56,43 @@ type Union2 struct { } } +type C struct { + A c.Int +} + +type D struct { + B c.Int +} + +type B struct { + CField C + DField D +} + +type F struct { + B c.Int +} + +type E struct { + FField F +} + +// https://github.com/goplus/llcppg/issues/514 +type A struct { + BField B + EField E +} + ===== llcppg.pub ===== +a A +b B +c C +d D +e E +f F +inner_struct InnerStruct struct1 Struct1 struct2 Struct2 +struct_with_nested StructWithNested union1 Union1 union2 Union2 \ No newline at end of file diff --git a/cl/internal/convert/_testdata/nested/hfile/temp.h b/cl/internal/convert/_testdata/nested/hfile/temp.h index 9d8a2c2b..570de213 100644 --- a/cl/internal/convert/_testdata/nested/hfile/temp.h +++ b/cl/internal/convert/_testdata/nested/hfile/temp.h @@ -10,6 +10,15 @@ struct struct1 } init; }; + +// https://github.com/goplus/llcppg/issues/514 +// named nested struct +struct struct_with_nested { + struct inner_struct { + long l; + } init; +}; + struct struct2 { char *b; @@ -47,4 +56,22 @@ union union2 char b[60]; struct2 rec; } init; -}; \ No newline at end of file +}; + + +// https://github.com/goplus/llcppg/issues/514 +struct a { + struct b { + struct c { + int a; + } c_field; + struct d { + int b; + } d_field; + } b_field; + struct e { + struct f { + int b; + } f_field; + } e_field; +}; From c269b9fc00bfb935d268989f6c9fa33453381942 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Fri, 18 Jul 2025 10:26:49 +0000 Subject: [PATCH 10/11] cl:case for https://github.com/goplus/llcppg/issues/507 --- .../_testdata/issue507/conf/llcppg.cfg | 6 ++ .../_testdata/issue507/gogensig.expect | 85 +++++++++++++++++++ .../convert/_testdata/issue507/hfile/temp.h | 71 ++++++++++++++++ 3 files changed, 162 insertions(+) create mode 100644 cl/internal/convert/_testdata/issue507/conf/llcppg.cfg create mode 100644 cl/internal/convert/_testdata/issue507/gogensig.expect create mode 100644 cl/internal/convert/_testdata/issue507/hfile/temp.h diff --git a/cl/internal/convert/_testdata/issue507/conf/llcppg.cfg b/cl/internal/convert/_testdata/issue507/conf/llcppg.cfg new file mode 100644 index 00000000..21381ce6 --- /dev/null +++ b/cl/internal/convert/_testdata/issue507/conf/llcppg.cfg @@ -0,0 +1,6 @@ +{ + "name": "issue507", + "include": ["temp.h"], + "libs": "$(pkg-config --libs xxx)", + "cplusplus":false +} diff --git a/cl/internal/convert/_testdata/issue507/gogensig.expect b/cl/internal/convert/_testdata/issue507/gogensig.expect new file mode 100644 index 00000000..31c76418 --- /dev/null +++ b/cl/internal/convert/_testdata/issue507/gogensig.expect @@ -0,0 +1,85 @@ +===== issue507_autogen_link.go ===== +package issue507 + +import _ "github.com/goplus/lib/c" + +const LLGoPackage string = "link: $(pkg-config --libs xxx);" + +===== temp.go ===== +package issue507 + +import ( + "github.com/goplus/lib/c" + _ "unsafe" +) + +type Ip6Addr struct { + Zone c.Int +} +type Ip6AddrT Ip6Addr + +type IpAddr struct { + UAddr struct { + Ip6 Ip6AddrT + } + Type c.Int +} +type IpAddrT IpAddr + +type Ip4Addr struct { + Addr c.Int +} +type Ip4AddrT Ip4Addr + +/** Args to LWIP_NSC_LINK_CHANGED callback */ + +type LinkChangedS struct { + State c.Int +} + +/** 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 { + OldAddress *IpAddrT + OldNetmask *IpAddrT + OldGw *IpAddrT +} + +/** 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 { + AddrIndex c.Int + OldState c.Int + Address *IpAddrT +} + +type NetifExtCallbackArgsT struct { + Ipv4Changed Ipv4ChangedS +} + +===== llcppg.pub ===== +ip4_addr Ip4Addr +ip4_addr_t Ip4AddrT +ip6_addr Ip6Addr +ip6_addr_t Ip6AddrT +ip_addr IpAddr +ip_addr_t IpAddrT +ipv4_changed_s Ipv4ChangedS +ipv6_addr_state_changed_s Ipv6AddrStateChangedS +ipv6_set_s Ipv6SetS +link_changed_s LinkChangedS +netif_ext_callback_args_t NetifExtCallbackArgsT +status_changed_s StatusChangedS \ No newline at end of file diff --git a/cl/internal/convert/_testdata/issue507/hfile/temp.h b/cl/internal/convert/_testdata/issue507/hfile/temp.h new file mode 100644 index 00000000..9debade6 --- /dev/null +++ b/cl/internal/convert/_testdata/issue507/hfile/temp.h @@ -0,0 +1,71 @@ + +struct ip6_addr +{ + int zone; +}; + +/** IPv6 address */ +typedef struct ip6_addr ip6_addr_t; + +typedef struct ip_addr +{ + union + { + ip6_addr_t ip6; + ip4_addr_t ip4; + } u_addr; + /** @ref lwip_ip_addr_type */ + int type; +} ip_addr_t; + +struct ip4_addr +{ + int addr; +}; + +/** ip4_addr_t uses a struct for convenience only, so that the same defines can + * operate both on ip4_addr_t as well as on ip4_addr_p_t. */ +typedef struct ip4_addr ip4_addr_t; + +typedef union +{ + /** Args to LWIP_NSC_LINK_CHANGED callback */ + struct link_changed_s + { + /** 1: up; 0: down */ + int state; + } link_changed; + /** Args to LWIP_NSC_STATUS_CHANGED callback */ + struct status_changed_s + { + /** 1: up; 0: down */ + int state; + } status_changed; + /** Args to LWIP_NSC_IPV4_ADDRESS_CHANGED|LWIP_NSC_IPV4_GATEWAY_CHANGED|LWIP_NSC_IPV4_NETMASK_CHANGED|LWIP_NSC_IPV4_SETTINGS_CHANGED callback */ + struct ipv4_changed_s + { + /** Old IPv4 address */ + const ip_addr_t *old_address; + const ip_addr_t *old_netmask; + const ip_addr_t *old_gw; + } ipv4_changed; + /** Args to LWIP_NSC_IPV6_SET callback */ + struct ipv6_set_s + { + /** Index of changed IPv6 address */ + int addr_index; + /** Old IPv6 address */ + const ip_addr_t *old_address; + } ipv6_set; + /** Args to LWIP_NSC_IPV6_ADDR_STATE_CHANGED callback */ + struct ipv6_addr_state_changed_s + { + /** Index of affected IPv6 address */ + int addr_index; + /** Old IPv6 address state */ + int old_state; + /** Affected IPv6 address */ + const ip_addr_t *address; + } ipv6_addr_state_changed; +} netif_ext_callback_args_t; + From ceac0a53c57402752eb1d8cf48f214b964c5fa7d Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Fri, 18 Jul 2025 11:32:14 +0000 Subject: [PATCH 11/11] GetChilds -> PostOrderVisitChildren --- _xtool/internal/parser/parser.go | 6 +++--- _xtool/internal/parser/parser_test.go | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/_xtool/internal/parser/parser.go b/_xtool/internal/parser/parser.go index 4292f126..7fb3cf73 100644 --- a/_xtool/internal/parser/parser.go +++ b/_xtool/internal/parser/parser.go @@ -757,7 +757,7 @@ func (ct *Converter) ProcessRecordDecl(cursor clang.Cursor) []ast.Decl { cursorName, cursorKind := getCursorDesc(cursor) ct.logln("ProcessRecordDecl: CursorName:", cursorName, "CursorKind:", cursorKind) - childs := GetChilds(cursor, func(child, parent clang.Cursor) bool { + childs := PostOrderVisitChildren(cursor, func(child, parent clang.Cursor) bool { return (child.Kind == clang.CursorStructDecl || child.Kind == clang.CursorUnionDecl) && child.IsAnonymous() == 0 }) @@ -971,11 +971,11 @@ func (ct *Converter) BuildScopingExpr(cursor clang.Cursor) ast.Expr { return buildScopingFromParts(parts) } -func GetChilds(cursor clang.Cursor, collect func(c, p clang.Cursor) bool) []clang.Cursor { +func PostOrderVisitChildren(cursor clang.Cursor, collect func(c, p clang.Cursor) bool) []clang.Cursor { var children []clang.Cursor clangutils.VisitChildren(cursor, func(child, parent clang.Cursor) clang.ChildVisitResult { if collect(child, parent) { - childs := GetChilds(child, collect) + childs := PostOrderVisitChildren(child, collect) children = append(children, childs[:]...) children = append(children, child) } diff --git a/_xtool/internal/parser/parser_test.go b/_xtool/internal/parser/parser_test.go index 059fe6c1..0e10a904 100644 --- a/_xtool/internal/parser/parser_test.go +++ b/_xtool/internal/parser/parser_test.go @@ -628,7 +628,7 @@ func compareOutput(t *testing.T, expected, actual string) { } } -func TestGetChilds(t *testing.T) { +func TestPostOrderVisitChildren(t *testing.T) { config := &clangutils.Config{ File: "./testdata/named_nested_struct/temp.h", Temp: false, @@ -655,7 +655,7 @@ func TestGetChilds(t *testing.T) { defer unit.Dispose() childStr := make([]string, 6) - childs := parser.GetChilds(unit.Cursor(), func(child, parent clang.Cursor) bool { + childs := parser.PostOrderVisitChildren(unit.Cursor(), func(child, parent clang.Cursor) bool { return child.Kind == clang.CursorStructDecl }) for i, child := range childs {