diff --git a/cxx-parser/__integration_test__/cxx_parser.integration.test.ts b/cxx-parser/__integration_test__/cxx_parser.integration.test.ts index e511b3a..3e32897 100644 --- a/cxx-parser/__integration_test__/cxx_parser.integration.test.ts +++ b/cxx-parser/__integration_test__/cxx_parser.integration.test.ts @@ -27,10 +27,10 @@ describe('cxx_parser', () => { fs.writeFileSync( file1Path, ` -struct AAA { - int a; -}; -` + struct AAA { + int a; + }; + ` ); let checkSum = generateChecksum([file1Path]); @@ -41,45 +41,45 @@ struct AAA { // TODO(littlegnal): Should move the tmp/*.h to the build dir in the future const expectedJson = ` - [ - { - "__TYPE":"CXXFile", - "file_path":"${preProcessParseFilesDir}/file1.h", - "nodes":[ + [ { - "__TYPE":"Struct", - "attributes":[], - "base_clazzs":[], - "comment":"", - "constructors":[], + "__TYPE":"CXXFile", "file_path":"${preProcessParseFilesDir}/file1.h", - "member_variables":[ + "nodes":[ { - "__TYPE":"MemberVariable", - "access_specifier":"", - "is_mutable":false, - "name":"a", - "type":{ - "__TYPE":"SimpleType", - "is_builtin_type":true, - "is_const":false, - "kind":100, - "name":"int", - "source":"int", - "template_arguments":[] - } + "__TYPE":"Struct", + "attributes":[], + "base_clazzs":[], + "comment":"", + "constructors":[], + "file_path":"${preProcessParseFilesDir}/file1.h", + "member_variables":[ + { + "__TYPE":"MemberVariable", + "access_specifier":"", + "is_mutable":false, + "name":"a", + "type":{ + "__TYPE":"SimpleType", + "is_builtin_type":true, + "is_const":false, + "kind":100, + "name":"int", + "source":"int", + "template_arguments":[] + } + } + ], + "methods":[], + "name":"AAA", + "namespaces":[], + "parent_name":"${preProcessParseFilesDir}/file1.h", + "source":"" } - ], - "methods":[], - "name":"AAA", - "namespaces":[], - "parent_name":"${preProcessParseFilesDir}/file1.h", - "source":"" + ] } ] - } - ] - `; + `; let json = dumpCXXAstJson(new TerraContext(tmpDir), [], [file1Path], []); @@ -88,6 +88,430 @@ struct AAA { // Use `JSON.parse` to parse the json string to avoid the format issue expect(JSON.parse(json)).toEqual(JSON.parse(expectedJson)); }); + + describe('parse typedef', () => { + it('c-style typedef struct', () => { + let file1Path = path.join(tmpDir, 'file1.h'); + + fs.writeFileSync( + file1Path, + ` + typedef struct AAA { + int a; + } AAA; + ` + ); + + let checkSum = generateChecksum([file1Path]); + let preProcessParseFilesDir = path.join( + cxxParserCacheDir, + `preProcess@${checkSum}` + ); + + // TODO(littlegnal): Should move the tmp/*.h to the build dir in the future + const expectedJson = ` + [ + { + "__TYPE":"CXXFile", + "file_path":"${preProcessParseFilesDir}/file1.h", + "nodes":[ + { + "__TYPE":"Struct", + "attributes":[], + "base_clazzs":[], + "comment":"", + "constructors":[], + "file_path":"${preProcessParseFilesDir}/file1.h", + "member_variables":[ + { + "__TYPE":"MemberVariable", + "access_specifier":"", + "is_mutable":false, + "name":"a", + "type":{ + "__TYPE":"SimpleType", + "is_builtin_type":true, + "is_const":false, + "kind":100, + "name":"int", + "source":"int", + "template_arguments":[] + } + } + ], + "methods":[], + "name":"AAA", + "namespaces":[], + "parent_name":"${preProcessParseFilesDir}/file1.h", + "source":"" + } + ] + } + ] + `; + + let json = dumpCXXAstJson( + new TerraContext(tmpDir), + [], + [file1Path], + [] + ); + + expect(fs.existsSync(preProcessParseFilesDir)).toBe(true); + + // Use `JSON.parse` to parse the json string to avoid the format issue + expect(JSON.parse(json)).toEqual(JSON.parse(expectedJson)); + }); + + it('c-style typedef struct with empty name', () => { + let file1Path = path.join(tmpDir, 'file1.h'); + + fs.writeFileSync( + file1Path, + ` + typedef struct { + int a; + } AAA; + ` + ); + + let checkSum = generateChecksum([file1Path]); + let preProcessParseFilesDir = path.join( + cxxParserCacheDir, + `preProcess@${checkSum}` + ); + + // TODO(littlegnal): Should move the tmp/*.h to the build dir in the future + const expectedJson = ` + [ + { + "__TYPE":"CXXFile", + "file_path":"${preProcessParseFilesDir}/file1.h", + "nodes":[ + { + "__TYPE":"Struct", + "attributes":[], + "base_clazzs":[], + "comment":"", + "constructors":[], + "file_path":"${preProcessParseFilesDir}/file1.h", + "member_variables":[ + { + "__TYPE":"MemberVariable", + "access_specifier":"", + "is_mutable":false, + "name":"a", + "type":{ + "__TYPE":"SimpleType", + "is_builtin_type":true, + "is_const":false, + "kind":100, + "name":"int", + "source":"int", + "template_arguments":[] + } + } + ], + "methods":[], + "name":"AAA", + "namespaces":[], + "parent_name":"${preProcessParseFilesDir}/file1.h", + "source":"" + } + ] + } + ] + `; + + let json = dumpCXXAstJson( + new TerraContext(tmpDir), + [], + [file1Path], + [] + ); + + expect(fs.existsSync(preProcessParseFilesDir)).toBe(true); + + // Use `JSON.parse` to parse the json string to avoid the format issue + expect(JSON.parse(json)).toEqual(JSON.parse(expectedJson)); + }); + + it('c-style typedef enum', () => { + let file1Path = path.join(tmpDir, 'file1.h'); + + fs.writeFileSync( + file1Path, + ` + typedef enum MyEnum { + A = 0, + } MyEnum; + ` + ); + + let checkSum = generateChecksum([file1Path]); + let preProcessParseFilesDir = path.join( + cxxParserCacheDir, + `preProcess@${checkSum}` + ); + + // TODO(littlegnal): Should move the tmp/*.h to the build dir in the future + const expectedJson = ` + [ + { + "__TYPE":"CXXFile", + "file_path":"${preProcessParseFilesDir}/file1.h", + "nodes":[ + { + "__TYPE":"Enumz", + "attributes":[], + "comment":"", + "enum_constants": [ + { + "__TYPE": "EnumConstant", + "name": "A", + "source": "0", + "value": "0" + } + ], + "file_path":"${preProcessParseFilesDir}/file1.h", + "name":"MyEnum", + "namespaces":[], + "parent_name":"${preProcessParseFilesDir}/file1.h", + "source":"" + } + ] + } + ] + `; + + let json = dumpCXXAstJson( + new TerraContext(tmpDir), + [], + [file1Path], + [] + ); + + expect(fs.existsSync(preProcessParseFilesDir)).toBe(true); + + // Use `JSON.parse` to parse the json string to avoid the format issue + expect(JSON.parse(json)).toEqual(JSON.parse(expectedJson)); + }); + + it('c-style typedef enum with empty name', () => { + let file1Path = path.join(tmpDir, 'file1.h'); + + fs.writeFileSync( + file1Path, + ` + typedef enum { + A = 0, + } MyEnum; + ` + ); + + let checkSum = generateChecksum([file1Path]); + let preProcessParseFilesDir = path.join( + cxxParserCacheDir, + `preProcess@${checkSum}` + ); + + // TODO(littlegnal): Should move the tmp/*.h to the build dir in the future + const expectedJson = ` + [ + { + "__TYPE":"CXXFile", + "file_path":"${preProcessParseFilesDir}/file1.h", + "nodes":[ + { + "__TYPE":"Enumz", + "attributes":[], + "comment":"", + "enum_constants": [ + { + "__TYPE": "EnumConstant", + "name": "A", + "source": "0", + "value": "0" + } + ], + "file_path":"${preProcessParseFilesDir}/file1.h", + "name":"MyEnum", + "namespaces":[], + "parent_name":"${preProcessParseFilesDir}/file1.h", + "source":"" + } + ] + } + ] + `; + + let json = dumpCXXAstJson( + new TerraContext(tmpDir), + [], + [file1Path], + [] + ); + + expect(fs.existsSync(preProcessParseFilesDir)).toBe(true); + + // Use `JSON.parse` to parse the json string to avoid the format issue + expect(JSON.parse(json)).toEqual(JSON.parse(expectedJson)); + }); + + it('normal typedef void*', () => { + let file1Path = path.join(tmpDir, 'file1.h'); + + fs.writeFileSync( + file1Path, + ` + typedef void* view_t; + ` + ); + + let checkSum = generateChecksum([file1Path]); + let preProcessParseFilesDir = path.join( + cxxParserCacheDir, + `preProcess@${checkSum}` + ); + + // TODO(littlegnal): Should move the tmp/*.h to the build dir in the future + const expectedJson = ` + [ + { + "__TYPE":"CXXFile", + "file_path":"${preProcessParseFilesDir}/file1.h", + "nodes":[ + { + "__TYPE": "TypeAlias", + "attributes": [], + "comment": "", + "file_path":"${preProcessParseFilesDir}/file1.h", + "name": "view_t", + "namespaces": [], + "parent_name":"${preProcessParseFilesDir}/file1.h", + "source": "", + "underlyingType": { + "__TYPE": "SimpleType", + "is_builtin_type": true, + "is_const": false, + "kind": 101, + "name": "void", + "source": "void*", + "template_arguments": [] + } + } + ] + } + ] + `; + + let json = dumpCXXAstJson( + new TerraContext(tmpDir), + [], + [file1Path], + [] + ); + + expect(fs.existsSync(preProcessParseFilesDir)).toBe(true); + + // Use `JSON.parse` to parse the json string to avoid the format issue + expect(JSON.parse(json)).toEqual(JSON.parse(expectedJson)); + }); + + it('normal typedef void* under typedef struct', () => { + let file1Path = path.join(tmpDir, 'file1.h'); + + fs.writeFileSync( + file1Path, + ` + struct AAA { + int a; + }; + + typedef void* view_t; + ` + ); + + let checkSum = generateChecksum([file1Path]); + let preProcessParseFilesDir = path.join( + cxxParserCacheDir, + `preProcess@${checkSum}` + ); + + // TODO(littlegnal): Should move the tmp/*.h to the build dir in the future + const expectedJson = ` + [ + { + "__TYPE":"CXXFile", + "file_path":"${preProcessParseFilesDir}/file1.h", + "nodes":[ + { + "__TYPE":"Struct", + "attributes":[], + "base_clazzs":[], + "comment":"", + "constructors":[], + "file_path":"${preProcessParseFilesDir}/file1.h", + "member_variables":[ + { + "__TYPE":"MemberVariable", + "access_specifier":"", + "is_mutable":false, + "name":"a", + "type":{ + "__TYPE":"SimpleType", + "is_builtin_type":true, + "is_const":false, + "kind":100, + "name":"int", + "source":"int", + "template_arguments":[] + } + } + ], + "methods":[], + "name":"AAA", + "namespaces":[], + "parent_name":"${preProcessParseFilesDir}/file1.h", + "source":"" + }, + { + "__TYPE": "TypeAlias", + "attributes": [], + "comment": "", + "file_path":"${preProcessParseFilesDir}/file1.h", + "name": "view_t", + "namespaces": [], + "parent_name":"${preProcessParseFilesDir}/file1.h", + "source": "", + "underlyingType": { + "__TYPE": "SimpleType", + "is_builtin_type": true, + "is_const": false, + "kind": 101, + "name": "void", + "source": "void*", + "template_arguments": [] + } + } + ] + } + ] + `; + + let json = dumpCXXAstJson( + new TerraContext(tmpDir), + [], + [file1Path], + [] + ); + + expect(fs.existsSync(preProcessParseFilesDir)).toBe(true); + + // Use `JSON.parse` to parse the json string to avoid the format issue + expect(JSON.parse(json)).toEqual(JSON.parse(expectedJson)); + }); + }); }); describe('CXXParser', () => { diff --git a/cxx-parser/cxx/terra/include/terra.hpp b/cxx-parser/cxx/terra/include/terra.hpp index 02c6d26..d7c5469 100644 --- a/cxx-parser/cxx/terra/include/terra.hpp +++ b/cxx-parser/cxx/terra/include/terra.hpp @@ -110,6 +110,7 @@ namespace terra auto &cpp_pointer_type = static_cast(cpp_type); to_simple_type(type, cpp_pointer_type.pointee(), true); type.kind = SimpleTypeKind::pointer_t; + break; } case cppast::cpp_type_kind::reference_t: @@ -557,11 +558,31 @@ namespace terra return true; } + // If C-style definitions are used to define structs or enums, such as, + // ``` + // typedef enum + // { + // kPreloadStatusCompleted = 0, + // kPreloadStatusFailed = 1, + // } PreloadStatusCode; + // ``` + // The above code will be parsed as an enum node with an empty name. Next, + // we need to fill the name of the type_alias_t to the previous node. + auto &last_node = cxx_file.nodes.back(); + bool isNeedFillPreNodeName = false; + std::string preNodeName = ""; + + // need fill the previous node name if name is empty + // if the name is same, do nothing + if (std::holds_alternative(last_node)) { auto &enumz = std::get(last_node); + preNodeName = enumz.name; + + isNeedFillPreNodeName = enumz.name.empty(); if (enumz.name.empty()) { enumz.name = cpp_type_alias.name(); @@ -571,6 +592,9 @@ namespace terra else if (std::holds_alternative(last_node)) { auto &clazz = std::get(last_node); + preNodeName = clazz.name; + + isNeedFillPreNodeName = clazz.name.empty(); if (clazz.name.empty()) { clazz.name = cpp_type_alias.name(); @@ -580,19 +604,27 @@ namespace terra else if (std::holds_alternative(last_node)) { auto &structt = std::get(last_node); - if (structt.name.empty()) + isNeedFillPreNodeName = structt.name.empty(); + preNodeName = structt.name; + if (isNeedFillPreNodeName) { structt.name = cpp_type_alias.name(); std::cout << "[type_alias_t] struct name: " << structt.name << std::endl; } } - else + + // It's the normal type alias, e.g., + // `typedef void* view_t` + std::string cn = cpp_type_alias.name(); + if (!isNeedFillPreNodeName && preNodeName != cn) { NodeType node = parse_type_alias(cpp_type_alias, namespaceList, file_path); cxx_file.nodes.push_back(node); std::cout << "[type_alias_t] type name: " << cpp_type_alias.name() << ", under type: " << cppast::to_string(cpp_type_alias.underlying_type()) << std::endl; } + + return true; } if (e.kind() == cppast::cpp_entity_kind::class_t && !info.is_old_entity())