From d2c44a0066f8b88e0d749ef9936fbbdc8cf800cb Mon Sep 17 00:00:00 2001 From: IDKWNTCMF Date: Thu, 20 Jul 2023 11:41:05 +0300 Subject: [PATCH 1/7] Generate declarations for unnamed fields of structs and unions --- server/src/Server.cpp | 4 +- server/src/Synchronizer.cpp | 11 +++-- server/src/Synchronizer.h | 3 +- .../SourceToHeaderMatchCallback.cpp | 47 ++++++++++++++++++- .../clang-utils/SourceToHeaderMatchCallback.h | 13 +++++ .../clang-utils/SourceToHeaderRewriter.cpp | 37 ++++++++++----- .../src/clang-utils/SourceToHeaderRewriter.h | 6 ++- server/src/types/Types.h | 1 + server/src/types/TypesResolver.cpp | 13 ++++- server/src/types/TypesResolver.h | 1 + 10 files changed, 111 insertions(+), 25 deletions(-) diff --git a/server/src/Server.cpp b/server/src/Server.cpp index ef5d606b9..f3560ccbe 100644 --- a/server/src/Server.cpp +++ b/server/src/Server.cpp @@ -203,10 +203,10 @@ Status Server::TestsGenServiceImpl::ProcessBaseTestRequest(BaseTestGen &testGen, &sizeContext.maximumAlignment, testGen.compileCommandsJsonPath, false); fetcher.fetchWithProgress(testGen.progressWriter, logMessage); + types::TypesHandler typesHandler{testGen.types, sizeContext}; SourceToHeaderRewriter(testGen.projectContext, testGen.getTargetBuildDatabase()->compilationDatabase, - fetcher.getStructsToDeclare(), testGen.serverBuildDir) + fetcher.getStructsToDeclare(), testGen.serverBuildDir, typesHandler) .generateTestHeaders(testGen.tests, testGen.progressWriter); - types::TypesHandler typesHandler{testGen.types, sizeContext}; testGen.progressWriter->writeProgress("Generating stub files", 0.0); StubGen stubGen(testGen); diff --git a/server/src/Synchronizer.cpp b/server/src/Synchronizer.cpp index e929b9f30..740a986cb 100644 --- a/server/src/Synchronizer.cpp +++ b/server/src/Synchronizer.cpp @@ -150,7 +150,7 @@ void Synchronizer::synchronize(const types::TypesHandler &typesHandler) { synchronizeStubs(outdatedStubs, typesHandler); } auto outdatedSourcePaths = getOutdatedSourcePaths(); - synchronizeWrappers(outdatedSourcePaths); + synchronizeWrappers(outdatedSourcePaths, typesHandler); } void Synchronizer::synchronizeStubs(StubSet &outdatedStubs, @@ -191,7 +191,7 @@ void Synchronizer::synchronizeStubs(StubSet &outdatedStubs, auto sourceToHeaderRewriter = SourceToHeaderRewriter(testGen->projectContext, testGen->getProjectBuildDatabase()->compilationDatabase, - stubFetcher.getStructsToDeclare(), testGen->serverBuildDir); + stubFetcher.getStructsToDeclare(), testGen->serverBuildDir, typesHandler); for (const StubOperator &outdatedStub : outdatedStubs) { fs::path stubPath = outdatedStub.getStubPath(testGen->projectContext); @@ -221,7 +221,8 @@ Synchronizer::createStubsCompilationDatabase(StubSet &stubFiles, return CompilationUtils::getCompilationDatabase(ccJsonStubDirPath); } -void Synchronizer::synchronizeWrappers(const CollectionUtils::FileSet &outdatedSourcePaths) const { +void Synchronizer::synchronizeWrappers(const CollectionUtils::FileSet &outdatedSourcePaths, + const types::TypesHandler &typesHandler) const { auto sourceFilesNeedToRegenerateWrappers = outdatedSourcePaths; for (fs::path const &sourceFilePath : getTargetSourceFiles()) { if (!CollectionUtils::contains(sourceFilesNeedToRegenerateWrappers, sourceFilePath)) { @@ -234,10 +235,10 @@ void Synchronizer::synchronizeWrappers(const CollectionUtils::FileSet &outdatedS } ExecUtils::doWorkWithProgress( sourceFilesNeedToRegenerateWrappers, testGen->progressWriter, - "Generating wrappers", [this](fs::path const &sourceFilePath) { + "Generating wrappers", [this, &typesHandler](fs::path const &sourceFilePath) { SourceToHeaderRewriter sourceToHeaderRewriter(testGen->projectContext, testGen->getProjectBuildDatabase()->compilationDatabase, nullptr, - testGen->serverBuildDir); + testGen->serverBuildDir, typesHandler); std::string wrapper = sourceToHeaderRewriter.generateWrapper(sourceFilePath); printer::SourceWrapperPrinter(Paths::getSourceLanguage(sourceFilePath)).print(testGen->projectContext, sourceFilePath, wrapper); }); diff --git a/server/src/Synchronizer.h b/server/src/Synchronizer.h index c7eb12fa3..fa3ff7581 100644 --- a/server/src/Synchronizer.h +++ b/server/src/Synchronizer.h @@ -37,7 +37,8 @@ class Synchronizer { void synchronizeStubs(std::unordered_set &outdatedStubs, const types::TypesHandler &typesHandler); - void synchronizeWrappers(const CollectionUtils::FileSet &outdatedSourcePaths) const; + void synchronizeWrappers(const CollectionUtils::FileSet &outdatedSourcePaths, + const types::TypesHandler &typesHandler) const; std::shared_ptr createStubsCompilationDatabase( diff --git a/server/src/clang-utils/SourceToHeaderMatchCallback.cpp b/server/src/clang-utils/SourceToHeaderMatchCallback.cpp index 218aa62d8..3256f1112 100644 --- a/server/src/clang-utils/SourceToHeaderMatchCallback.cpp +++ b/server/src/clang-utils/SourceToHeaderMatchCallback.cpp @@ -19,11 +19,14 @@ SourceToHeaderMatchCallback::SourceToHeaderMatchCallback(utbot::ProjectContext p fs::path sourceFilePath, raw_ostream *externalStream, raw_ostream *internalStream, + raw_ostream *unnamedTypeDeclsStream, raw_ostream *wrapperStream, + const types::TypesHandler &typesHandler, bool forStubHeader) : projectContext(std::move(projectContext)), sourceFilePath(std::move(sourceFilePath)), externalStream(externalStream), - internalStream(internalStream), wrapperStream(wrapperStream), forStubHeader(forStubHeader) { + internalStream(internalStream), unnamedTypeDeclsStream(unnamedTypeDeclsStream), + wrapperStream(wrapperStream), typesHandler(typesHandler), forStubHeader(forStubHeader) { } void SourceToHeaderMatchCallback::run(const ast_matchers::MatchFinder::MatchResult &Result) { @@ -128,12 +131,14 @@ void SourceToHeaderMatchCallback::checkVarDecl(const MatchFinder::MatchResult &R void SourceToHeaderMatchCallback::handleStruct(const RecordDecl *decl) { print(decl); + generateUnnamedTypeDecls(decl); } void SourceToHeaderMatchCallback::handleEnum(const EnumDecl *decl) { print(decl); } void SourceToHeaderMatchCallback::handleUnion(const RecordDecl *decl) { print(decl); + generateUnnamedTypeDecls(decl); } void SourceToHeaderMatchCallback::handleTypedef(const TypedefDecl *decl) { @@ -301,6 +306,36 @@ void SourceToHeaderMatchCallback::generateWrapper(const VarDecl *decl) const { *wrapperStream << wrapperPointerDecl << " = &" << name << ";\n"; } +void SourceToHeaderMatchCallback::generateUnnamedTypeDecls(const clang::RecordDecl *decl) const { + if (unnamedTypeDeclsStream == nullptr) { + return; + } + clang::ASTContext const &context = decl->getASTContext(); + clang::QualType canonicalType = context.getTypeDeclType(decl).getCanonicalType(); + uint64_t id = types::Type::getIdFromCanonicalType(canonicalType); + if (typesHandler.isStructLike(id)) { + types::StructInfo info = typesHandler.getStructInfo(id); + generateUnnamedTypeDeclsForFields(info); + } +} + +void SourceToHeaderMatchCallback::generateUnnamedTypeDeclsForFields(const types::StructInfo &info) const { + for (const types::Field &field : info.fields) { + if (!field.unnamedType || field.anonymous) { + continue; + } + if (typesHandler.isStructLike(field.type)) { + types::StructInfo fieldInfo = typesHandler.getStructInfo(field.type); + printUnnamedTypeDecl(info.name, field.name, fieldInfo.name); + generateUnnamedTypeDeclsForFields(fieldInfo); + } + if (typesHandler.isEnum(field.type)) { + types::EnumInfo enumInfo = typesHandler.getEnumInfo(field.type); + printUnnamedTypeDecl(info.name, field.name, enumInfo.name); + } + } +} + void SourceToHeaderMatchCallback::printReturn(const FunctionDecl *decl, std::string const &name, @@ -320,6 +355,16 @@ void SourceToHeaderMatchCallback::printReturn(const FunctionDecl *decl, *stream << printer.ss.str(); } +void SourceToHeaderMatchCallback::printUnnamedTypeDecl(const std::string &structName, + const std::string &fieldName, + const std::string &typeName) const { + std::string typeDecl = StringUtils::stringFormat( + "typedef decltype(%s::%s) %s;\n", + structName, fieldName, typeName + ); + *unnamedTypeDeclsStream << typeDecl; +} + std::string SourceToHeaderMatchCallback::decorate(std::string_view name) const { return forStubHeader ? std::string(name) : NameDecorator::decorate(name); } diff --git a/server/src/clang-utils/SourceToHeaderMatchCallback.h b/server/src/clang-utils/SourceToHeaderMatchCallback.h index 1056b3719..4f082f111 100644 --- a/server/src/clang-utils/SourceToHeaderMatchCallback.h +++ b/server/src/clang-utils/SourceToHeaderMatchCallback.h @@ -21,10 +21,13 @@ class SourceToHeaderMatchCallback : public clang::ast_matchers::MatchFinder::Mat fs::path sourceFilePath; llvm::raw_ostream *const externalStream = nullptr; llvm::raw_ostream *const internalStream = nullptr; + llvm::raw_ostream *const unnamedTypeDeclsStream = nullptr; llvm::raw_ostream *const wrapperStream = nullptr; std::unordered_set variables{}; + const types::TypesHandler &typesHandler; + bool forStubHeader; public: SourceToHeaderMatchCallback( @@ -32,7 +35,9 @@ class SourceToHeaderMatchCallback : public clang::ast_matchers::MatchFinder::Mat fs::path sourceFilePath, llvm::raw_ostream *externalStream, llvm::raw_ostream *internalStream, + llvm::raw_ostream *unnamedTypeDeclsStream, llvm::raw_ostream *wrapperStream, + const types::TypesHandler &typesHandler, bool forStubHeader); void run(const MatchFinder::MatchResult &Result) override; @@ -72,6 +77,10 @@ class SourceToHeaderMatchCallback : public clang::ast_matchers::MatchFinder::Mat std::string const &name, llvm::raw_ostream *stream) const; + void printUnnamedTypeDecl(const std::string &structName, + const std::string &fieldName, + const std::string &typeName) const; + void generateWrapper(const clang::FunctionDecl *decl) const; void generateWrapper(const clang::VarDecl *decl) const; @@ -80,6 +89,10 @@ class SourceToHeaderMatchCallback : public clang::ast_matchers::MatchFinder::Mat void generateInternal(const clang::VarDecl *decl) const; + void generateUnnamedTypeDeclsForFields(const types::StructInfo &info) const; + + void generateUnnamedTypeDecls(const clang::RecordDecl *decl) const; + std::string getRenamedDeclarationAsString(const clang::NamedDecl *decl, clang::PrintingPolicy const &policy, std::string const &name) const; diff --git a/server/src/clang-utils/SourceToHeaderRewriter.cpp b/server/src/clang-utils/SourceToHeaderRewriter.cpp index 91630d956..f5c3f2fc1 100644 --- a/server/src/clang-utils/SourceToHeaderRewriter.cpp +++ b/server/src/clang-utils/SourceToHeaderRewriter.cpp @@ -15,20 +15,26 @@ SourceToHeaderRewriter::SourceToHeaderRewriter( utbot::ProjectContext projectContext, const std::shared_ptr &compilationDatabase, std::shared_ptr structsToDeclare, - fs::path serverBuildDir) + fs::path serverBuildDir, + const types::TypesHandler &typesHandler) : projectContext(std::move(projectContext)), clangToolRunner(compilationDatabase), structsToDeclare(structsToDeclare), - serverBuildDir(std::move(serverBuildDir)) { + serverBuildDir(std::move(serverBuildDir)), typesHandler(typesHandler) { } std::unique_ptr SourceToHeaderRewriter::createFactory(llvm::raw_ostream *externalStream, llvm::raw_ostream *internalStream, + llvm::raw_ostream *unnamedTypeDeclsStream, llvm::raw_ostream *wrapperStream, fs::path sourceFilePath, bool forStubHeader) { + if (Paths::isCXXFile(sourceFilePath)) { + externalStream = nullptr; + internalStream = nullptr; + } fetcherInstance = std::make_unique( - projectContext, sourceFilePath, externalStream, internalStream, wrapperStream, forStubHeader); + projectContext, sourceFilePath, externalStream, internalStream, unnamedTypeDeclsStream, wrapperStream, typesHandler, forStubHeader); finder = std::make_unique(); finder->addMatcher(Matchers::anyToplevelDeclarationMatcher, fetcherInstance.get()); return clang::tooling::newFrontendActionFactory(finder.get()); @@ -40,8 +46,10 @@ SourceToHeaderRewriter::generateSourceDeclarations(const fs::path &sourceFilePat llvm::raw_string_ostream externalStream(externalDeclarations); std::string internalDeclarations; llvm::raw_string_ostream internalStream(internalDeclarations); + std::string unnamedTypeDeclarations; + llvm::raw_string_ostream unnamedTypeDeclsStream(unnamedTypeDeclarations); - auto factory = createFactory(&externalStream, &internalStream, nullptr, sourceFilePath, forStubHeader); + auto factory = createFactory(&externalStream, &internalStream, &unnamedTypeDeclsStream, nullptr, sourceFilePath, forStubHeader); if (CollectionUtils::containsKey(*structsToDeclare, sourceFilePath)) { std::stringstream newContentStream; @@ -57,14 +65,17 @@ SourceToHeaderRewriter::generateSourceDeclarations(const fs::path &sourceFilePat } externalStream.flush(); internalStream.flush(); + unnamedTypeDeclsStream.flush(); - return { externalDeclarations, internalDeclarations }; + return { externalDeclarations, internalDeclarations, unnamedTypeDeclarations }; } std::string SourceToHeaderRewriter::generateTestHeader(const fs::path &sourceFilePath, const Tests &test) { MEASURE_FUNCTION_EXECUTION_TIME + auto sourceDeclarations = generateSourceDeclarations(sourceFilePath, false); + if (Paths::isCXXFile(sourceFilePath)) { auto sourceFileToInclude = sourceFilePath; if (test.mainHeader.has_value()) { @@ -73,12 +84,11 @@ std::string SourceToHeaderRewriter::generateTestHeader(const fs::path &sourceFil } sourceFileToInclude = fs::relative(sourceFilePath, test.testHeaderFilePath.parent_path()); return StringUtils::stringFormat("#define main main__\n\n" - "#include \"%s\"\n\n", - sourceFileToInclude); + "#include \"%s\"\n\n" + "%s\n", + sourceFileToInclude, sourceDeclarations.unnamedTypeDeclarations); } - auto sourceDeclarations = generateSourceDeclarations(sourceFilePath, false); - return StringUtils::stringFormat( "%s\n" "namespace %s {\n" @@ -88,13 +98,14 @@ std::string SourceToHeaderRewriter::generateTestHeader(const fs::path &sourceFil "%s\n" "%s\n" "%s\n" - "}\n" - "%s\n", + "%s\n" + "\n%s" + "}\n", Copyright::GENERATED_C_CPP_FILE_HEADER, PrinterUtils::TEST_NAMESPACE, NameDecorator::DEFINES_CODE, PrinterUtils::DEFINES_FOR_C_KEYWORDS, PrinterUtils::KNOWN_IMPLICIT_RECORD_DECLS_CODE, sourceDeclarations.externalDeclarations, sourceDeclarations.internalDeclarations, - NameDecorator::UNDEF_WCHAR_T, NameDecorator::UNDEFS_CODE); + NameDecorator::UNDEF_WCHAR_T, NameDecorator::UNDEFS_CODE, sourceDeclarations.unnamedTypeDeclarations); } std::string SourceToHeaderRewriter::generateStubHeader(const fs::path &sourceFilePath) { @@ -122,7 +133,7 @@ std::string SourceToHeaderRewriter::generateWrapper(const fs::path &sourceFilePa } std::string result; llvm::raw_string_ostream wrapperStream(result); - auto factory = createFactory(nullptr, nullptr, &wrapperStream, sourceFilePath, false); + auto factory = createFactory(nullptr, nullptr, nullptr, &wrapperStream, sourceFilePath, false); clangToolRunner.run(sourceFilePath, factory.get()); wrapperStream.flush(); return result; diff --git a/server/src/clang-utils/SourceToHeaderRewriter.h b/server/src/clang-utils/SourceToHeaderRewriter.h index 7405582a8..d9867fe10 100644 --- a/server/src/clang-utils/SourceToHeaderRewriter.h +++ b/server/src/clang-utils/SourceToHeaderRewriter.h @@ -24,6 +24,7 @@ class SourceToHeaderRewriter { fs::path projectPath; fs::path serverBuildDir; std::shared_ptr structsToDeclare; + const types::TypesHandler &typesHandler; std::unique_ptr fetcherInstance; std::unique_ptr finder; @@ -31,6 +32,7 @@ class SourceToHeaderRewriter { std::unique_ptr createFactory(llvm::raw_ostream *externalStream, llvm::raw_ostream *internalStream, + llvm::raw_ostream *unnamedTypeDeclsStream, llvm::raw_ostream *wrapperStream, fs::path sourceFilePath, bool forStubHeader); @@ -39,6 +41,7 @@ class SourceToHeaderRewriter { struct SourceDeclarations { std::string externalDeclarations; std::string internalDeclarations; + std::string unnamedTypeDeclarations; }; friend class SourceToHeaderMatchCallback; @@ -47,7 +50,8 @@ class SourceToHeaderRewriter { utbot::ProjectContext projectContext, const std::shared_ptr &compilationDatabase, std::shared_ptr structsToDeclare, - fs::path serverBuildDir); + fs::path serverBuildDir, + const types::TypesHandler &typesHandler); SourceDeclarations generateSourceDeclarations(const fs::path &sourceFilePath, bool forStubHeader); diff --git a/server/src/types/Types.h b/server/src/types/Types.h index 634d2db8f..5c6b007a2 100644 --- a/server/src/types/Types.h +++ b/server/src/types/Types.h @@ -282,6 +282,7 @@ namespace types { struct Field { types::Type type; + bool unnamedType; bool anonymous; std::string name; /// size in @b bits diff --git a/server/src/types/TypesResolver.cpp b/server/src/types/TypesResolver.cpp index 79d4049cf..181e67ffb 100644 --- a/server/src/types/TypesResolver.cpp +++ b/server/src/types/TypesResolver.cpp @@ -63,16 +63,23 @@ std::string TypesResolver::getFullname(const clang::TagDecl *TD, const clang::Qu uint64_t id, const fs::path &sourceFilePath) { auto pp = clang::PrintingPolicy(clang::LangOptions()); pp.SuppressTagKeyword = true; - std::string currentStructName = canonicalType.getNonReferenceType().getUnqualifiedType().getAsString(pp); + bool typeDeclNeeded = canonicalType->hasUnnamedOrLocalType() && !fieldName[id].empty(); + std::string currentStructName = typeDeclNeeded + ? fieldName[id] + "_t" + : canonicalType.getNonReferenceType().getUnqualifiedType().getAsString(pp); fullname.insert(std::make_pair(id, currentStructName)); - if (Paths::getSourceLanguage(sourceFilePath) == utbot::Language::C) { + if (Paths::getSourceLanguage(sourceFilePath) == utbot::Language::C || typeDeclNeeded) { if (const auto *parentNode = llvm::dyn_cast(TD->getLexicalParent())) { clang::QualType parentCanonicalType = parentNode->getASTContext().getTypeDeclType( parentNode).getCanonicalType(); uint64_t parentID = types::Type::getIdFromCanonicalType(parentCanonicalType); if (!fullname[parentID].empty()) { fullname[id] = fullname[parentID] + "::" + fullname[id]; + if (typeDeclNeeded) { + StringUtils::replaceFirst(fullname[id], "_t::", "_"); + StringUtils::replaceAll(fullname[id], "::", "_"); + } } } } @@ -132,6 +139,8 @@ void TypesResolver::resolveStructEx(const clang::RecordDecl *D, const std::strin const clang::QualType paramType = F->getType().getCanonicalType(); field.type = types::Type(paramType, paramType.getAsString(), sourceManager); + field.unnamedType = field.type.isUnnamed(); + fieldName[field.type.getId()] = field.name; if (field.type.isPointerToFunction()) { structInfo.functionFields[field.name] = ParamsHandler::getFunctionPointerDeclaration( F->getFunctionType(), field.name, sourceManager, diff --git a/server/src/types/TypesResolver.h b/server/src/types/TypesResolver.h index badbc0b4c..8c5546569 100644 --- a/server/src/types/TypesResolver.h +++ b/server/src/types/TypesResolver.h @@ -15,6 +15,7 @@ class TypesResolver { private: const Fetcher *const parent; std::map fullname; + std::map fieldName; public: explicit TypesResolver(Fetcher const *parent); From e96ef763e47a8bf51b3398a63b0fa699ccd02bb4 Mon Sep 17 00:00:00 2001 From: IDKWNTCMF Date: Thu, 20 Jul 2023 12:18:47 +0300 Subject: [PATCH 2/7] Add test for this case --- server/src/types/Types.h | 4 +-- server/src/types/TypesResolver.cpp | 2 +- server/test/framework/Server_Tests.cpp | 4 ++- server/test/framework/Syntax_Tests.cpp | 28 +++++++++++++++++++ server/test/suites/syntax/struct_with_union.c | 13 +++++++++ server/test/suites/syntax/struct_with_union.h | 13 +++++++++ 6 files changed, 60 insertions(+), 4 deletions(-) diff --git a/server/src/types/Types.h b/server/src/types/Types.h index 5c6b007a2..be4b16962 100644 --- a/server/src/types/Types.h +++ b/server/src/types/Types.h @@ -367,7 +367,7 @@ namespace types { size_t maximumAlignment = 16; /// maximumAlignment in @b bytes }; - explicit TypesHandler(TypeMaps &types, SizeContext sizeContext) + explicit TypesHandler(const TypeMaps &types, SizeContext sizeContext) : typeMaps(types), sizeContext(sizeContext){}; /** @@ -651,7 +651,7 @@ namespace types { }; private: - TypeMaps &typeMaps; + const TypeMaps &typeMaps; SizeContext sizeContext; mutable tsl::ordered_set recursiveCheckStarted{}; mutable std::unordered_maphasUnnamedOrLocalType() && !fieldName[id].empty(); std::string currentStructName = typeDeclNeeded - ? fieldName[id] + "_t" + ? StringUtils::stringFormat("%s_t", fieldName[id]) : canonicalType.getNonReferenceType().getUnqualifiedType().getAsString(pp); fullname.insert(std::make_pair(id, currentStructName)); diff --git a/server/test/framework/Server_Tests.cpp b/server/test/framework/Server_Tests.cpp index 736acb9fa..710a6c634 100644 --- a/server/test/framework/Server_Tests.cpp +++ b/server/test/framework/Server_Tests.cpp @@ -85,8 +85,10 @@ namespace { auto compilationDatabase = CompilationUtils::getCompilationDatabase(buildPath); auto structsToDeclare = std::make_shared(); + types::TypesHandler::SizeContext sizeContext; + types::TypesHandler typesHandler{testGen.types, sizeContext}; SourceToHeaderRewriter sourceToHeaderRewriter(testGen.projectContext, compilationDatabase, - structsToDeclare, serverBuildDir); + structsToDeclare, serverBuildDir, typesHandler); std::string wrapper = sourceToHeaderRewriter.generateWrapper(sourceFile); printer::SourceWrapperPrinter(Paths::getSourceLanguage(sourceFile)).print(testGen.projectContext, sourceFile, wrapper); diff --git a/server/test/framework/Syntax_Tests.cpp b/server/test/framework/Syntax_Tests.cpp index c5a32ccc4..b92acd790 100644 --- a/server/test/framework/Syntax_Tests.cpp +++ b/server/test/framework/Syntax_Tests.cpp @@ -1893,6 +1893,34 @@ namespace { ); } + TEST_F(Syntax_Test, Support_Struct_with_Union_Of_Unnamed_Type) { + auto [testGen, status] = createTestForFunction(struct_with_union_c, 42); + + ASSERT_TRUE(status.ok()) << status.error_message(); + + checkTestCasePredicates( + testGen.tests.at(struct_with_union_c).methods.begin().value().testCases, + std::vector( + {[] (const tests::Tests::MethodTestCase& testCase) { + return stoi(testCase.paramValues[0].view->getEntryValue(nullptr)) < + stoi(testCase.paramValues[1].view->getEntryValue(nullptr)) && + testCase.returnValue.view->getEntryValue(nullptr) == "{{{'\\x99', -2.530171e-98}}}"; + }, + [] (const tests::Tests::MethodTestCase& testCase) { + return stoi(testCase.paramValues[0].view->getEntryValue(nullptr)) == + stoi(testCase.paramValues[1].view->getEntryValue(nullptr)) && + StringUtils::startsWith(testCase.returnValue.view->getEntryValue(nullptr), + "{from_bytes({"); + }, + [] (const tests::Tests::MethodTestCase& testCase) { + return stoi(testCase.paramValues[0].view->getEntryValue(nullptr)) > + stoi(testCase.paramValues[1].view->getEntryValue(nullptr)) && + testCase.returnValue.view->getEntryValue(nullptr) == "{{{'\\0', -2.530171e-98}}}"; + } + }) + ); + } + TEST_F(Syntax_Test, length_of_linked_list3) { auto [testGen, status] = createTestForFunction(linked_list_c, 3); diff --git a/server/test/suites/syntax/struct_with_union.c b/server/test/suites/syntax/struct_with_union.c index 121e38253..1553fe078 100644 --- a/server/test/suites/syntax/struct_with_union.c +++ b/server/test/suites/syntax/struct_with_union.c @@ -38,3 +38,16 @@ struct StructWithStructInUnion struct_with_struct_in_union_as_return_type(int a, } return ans; } + +struct StructWithUnionOfUnnamedType struct_with_union_of_unnamed_type_as_return_type(int a, int b) { + struct StructWithUnionOfUnnamedType ans; + if (a > b) { + ans.un.ptr = 0; + } else if (a < b) { + ans.un.x = 153; + } else { + ans.un.ds.c = 'k'; + ans.un.ds.d = 1.0101; + } + return ans; +} diff --git a/server/test/suites/syntax/struct_with_union.h b/server/test/suites/syntax/struct_with_union.h index cbef18a90..fa009b972 100644 --- a/server/test/suites/syntax/struct_with_union.h +++ b/server/test/suites/syntax/struct_with_union.h @@ -39,10 +39,23 @@ struct StructWithStructInUnion { } un; }; +struct StructWithUnionOfUnnamedType { + union { + int x; + struct { + char c; + double d; + } ds; + long long *ptr; + } un; +}; + struct StructWithUnion struct_with_union_as_return_type(int t); struct StructWithUnionInUnion struct_with_union_in_union_as_return_type(int a, int b); struct StructWithStructInUnion struct_with_struct_in_union_as_return_type(int a, int b); +struct StructWithUnionOfUnnamedType struct_with_union_of_unnamed_type_as_return_type(int a, int b); + #endif From 4bbea7deeffd5f937a8a71bd0a0c6e3bc681c187 Mon Sep 17 00:00:00 2001 From: IDKWNTCMF Date: Thu, 20 Jul 2023 18:31:59 +0300 Subject: [PATCH 3/7] Fix getFullname for unnamed types --- server/src/types/TypesResolver.cpp | 9 +++------ server/src/types/TypesResolver.h | 1 - server/test/framework/Syntax_Tests.cpp | 2 +- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/server/src/types/TypesResolver.cpp b/server/src/types/TypesResolver.cpp index 52a1e25a8..d48be498c 100644 --- a/server/src/types/TypesResolver.cpp +++ b/server/src/types/TypesResolver.cpp @@ -63,10 +63,8 @@ std::string TypesResolver::getFullname(const clang::TagDecl *TD, const clang::Qu uint64_t id, const fs::path &sourceFilePath) { auto pp = clang::PrintingPolicy(clang::LangOptions()); pp.SuppressTagKeyword = true; - bool typeDeclNeeded = canonicalType->hasUnnamedOrLocalType() && !fieldName[id].empty(); - std::string currentStructName = typeDeclNeeded - ? StringUtils::stringFormat("%s_t", fieldName[id]) - : canonicalType.getNonReferenceType().getUnqualifiedType().getAsString(pp); + bool typeDeclNeeded = canonicalType->hasUnnamedOrLocalType() && !fullname[id].empty(); + std::string currentStructName = canonicalType.getNonReferenceType().getUnqualifiedType().getAsString(pp); fullname.insert(std::make_pair(id, currentStructName)); if (Paths::getSourceLanguage(sourceFilePath) == utbot::Language::C || typeDeclNeeded) { @@ -77,7 +75,6 @@ std::string TypesResolver::getFullname(const clang::TagDecl *TD, const clang::Qu if (!fullname[parentID].empty()) { fullname[id] = fullname[parentID] + "::" + fullname[id]; if (typeDeclNeeded) { - StringUtils::replaceFirst(fullname[id], "_t::", "_"); StringUtils::replaceAll(fullname[id], "::", "_"); } } @@ -140,7 +137,7 @@ void TypesResolver::resolveStructEx(const clang::RecordDecl *D, const std::strin const clang::QualType paramType = F->getType().getCanonicalType(); field.type = types::Type(paramType, paramType.getAsString(), sourceManager); field.unnamedType = field.type.isUnnamed(); - fieldName[field.type.getId()] = field.name; + fullname[field.type.getId()] = field.name; if (field.type.isPointerToFunction()) { structInfo.functionFields[field.name] = ParamsHandler::getFunctionPointerDeclaration( F->getFunctionType(), field.name, sourceManager, diff --git a/server/src/types/TypesResolver.h b/server/src/types/TypesResolver.h index 8c5546569..badbc0b4c 100644 --- a/server/src/types/TypesResolver.h +++ b/server/src/types/TypesResolver.h @@ -15,7 +15,6 @@ class TypesResolver { private: const Fetcher *const parent; std::map fullname; - std::map fieldName; public: explicit TypesResolver(Fetcher const *parent); diff --git a/server/test/framework/Syntax_Tests.cpp b/server/test/framework/Syntax_Tests.cpp index b92acd790..10463774e 100644 --- a/server/test/framework/Syntax_Tests.cpp +++ b/server/test/framework/Syntax_Tests.cpp @@ -1910,7 +1910,7 @@ namespace { return stoi(testCase.paramValues[0].view->getEntryValue(nullptr)) == stoi(testCase.paramValues[1].view->getEntryValue(nullptr)) && StringUtils::startsWith(testCase.returnValue.view->getEntryValue(nullptr), - "{from_bytes({"); + "{from_bytes({"); }, [] (const tests::Tests::MethodTestCase& testCase) { return stoi(testCase.paramValues[0].view->getEntryValue(nullptr)) > From 9eda6ffbc9df8e4e5fdabe2b375bbf88c7fc8431 Mon Sep 17 00:00:00 2001 From: IDKWNTCMF Date: Thu, 20 Jul 2023 20:47:39 +0300 Subject: [PATCH 4/7] Some fixes --- server/src/types/TypesResolver.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/src/types/TypesResolver.cpp b/server/src/types/TypesResolver.cpp index d48be498c..3d1dcb174 100644 --- a/server/src/types/TypesResolver.cpp +++ b/server/src/types/TypesResolver.cpp @@ -137,7 +137,9 @@ void TypesResolver::resolveStructEx(const clang::RecordDecl *D, const std::strin const clang::QualType paramType = F->getType().getCanonicalType(); field.type = types::Type(paramType, paramType.getAsString(), sourceManager); field.unnamedType = field.type.isUnnamed(); - fullname[field.type.getId()] = field.name; + if (field.unnamedType && !field.anonymous) { + fullname[field.type.getId()] = field.name; + } if (field.type.isPointerToFunction()) { structInfo.functionFields[field.name] = ParamsHandler::getFunctionPointerDeclaration( F->getFunctionType(), field.name, sourceManager, From 1c5d6e2fbfcfb61688d4ed8571910adaa0771880 Mon Sep 17 00:00:00 2001 From: IDKWNTCMF Date: Fri, 21 Jul 2023 13:39:13 +0300 Subject: [PATCH 5/7] Add server test --- server/test/framework/Server_Tests.cpp | 38 +++++++++++++++++++ server/test/suites/server/CMakeLists.txt | 1 + server/test/suites/server/struct_with_union.c | 14 +++++++ server/test/suites/server/struct_with_union.h | 17 +++++++++ 4 files changed, 70 insertions(+) create mode 100644 server/test/suites/server/struct_with_union.c create mode 100644 server/test/suites/server/struct_with_union.h diff --git a/server/test/framework/Server_Tests.cpp b/server/test/framework/Server_Tests.cpp index 710a6c634..bfcb28d7a 100644 --- a/server/test/framework/Server_Tests.cpp +++ b/server/test/framework/Server_Tests.cpp @@ -38,6 +38,7 @@ namespace { fs::path pointer_parameters_c = getTestFilePath("pointer_parameters.c"); fs::path simple_structs_c = getTestFilePath("simple_structs.c"); fs::path simple_unions_c = getTestFilePath("simple_unions.c"); + fs::path struct_with_union_c = getTestFilePath("struct_with_union.c"); fs::path types_c = getTestFilePath("types.c"); fs::path inner_basic_functions_c = getTestFilePath("inner/inner_basic_functions.c"); fs::path pointer_return_c = getTestFilePath("pointer_return.c"); @@ -455,6 +456,35 @@ namespace { } } + void checkStructWithUnion_C(BaseTestGen &testGen) { + for (const auto &[methodName, methodDescription] : + testGen.tests.at(struct_with_union_c).methods) { + if (methodName == "struct_with_union_of_unnamed_type_as_return_type") { + checkTestCasePredicates( + testGen.tests.at(struct_with_union_c).methods.begin().value().testCases, + std::vector( + {[] (const tests::Tests::MethodTestCase& testCase) { + return stoi(testCase.paramValues[0].view->getEntryValue(nullptr)) < + stoi(testCase.paramValues[1].view->getEntryValue(nullptr)) && + testCase.returnValue.view->getEntryValue(nullptr) == "{{{'\\x99', -2.530171e-98}}}"; + }, + [] (const tests::Tests::MethodTestCase& testCase) { + return stoi(testCase.paramValues[0].view->getEntryValue(nullptr)) == + stoi(testCase.paramValues[1].view->getEntryValue(nullptr)) && + StringUtils::startsWith(testCase.returnValue.view->getEntryValue(nullptr), + "{from_bytes({"); + }, + [] (const tests::Tests::MethodTestCase& testCase) { + return stoi(testCase.paramValues[0].view->getEntryValue(nullptr)) > + stoi(testCase.paramValues[1].view->getEntryValue(nullptr)) && + testCase.returnValue.view->getEntryValue(nullptr) == "{{{'\\0', -2.530171e-98}}}"; + } + }), + methodName); + } + } + } + void checkInnerBasicFunctions_C(BaseTestGen &testGen) { for (const auto &[methodName, methodDescription] : testGen.tests.at(inner_basic_functions_c).methods) { @@ -874,6 +904,14 @@ namespace { checkSimpleUnions_C(testGen); } + TEST_F(Server_Test, Struct_With_Union) { + auto [testGen, status] = performFeatureFileTestsRequest(struct_with_union_c); + ASSERT_TRUE(status.ok()) << status.error_message(); + auto testFilePaths = CollectionUtils::getKeys(testGen.tests); + EXPECT_TRUE(!testFilePaths.empty()) << "Generated test files are missing."; + checkStructWithUnion_C(testGen); + } + TEST_F(Server_Test, Pointer_Parameters) { auto [testGen, status] = performFeatureFileTestsRequest(pointer_parameters_c); ASSERT_TRUE(status.ok()) << status.error_message(); diff --git a/server/test/suites/server/CMakeLists.txt b/server/test/suites/server/CMakeLists.txt index 461869a83..9195dc522 100644 --- a/server/test/suites/server/CMakeLists.txt +++ b/server/test/suites/server/CMakeLists.txt @@ -24,6 +24,7 @@ add_executable(server pointer_return.c simple_structs.c simple_unions.c + struct_with_union.c complex_structs.c typedefs.c types.c diff --git a/server/test/suites/server/struct_with_union.c b/server/test/suites/server/struct_with_union.c new file mode 100644 index 000000000..74ca7342c --- /dev/null +++ b/server/test/suites/server/struct_with_union.c @@ -0,0 +1,14 @@ +#include "struct_with_union.h" + +struct StructWithUnionOfUnnamedType struct_with_union_of_unnamed_type_as_return_type(int a, int b) { + struct StructWithUnionOfUnnamedType ans; + if (a > b) { + ans.un.ptr = 0; + } else if (a < b) { + ans.un.x = 153; + } else { + ans.un.ds.c = 'k'; + ans.un.ds.d = 1.0101; + } + return ans; +} \ No newline at end of file diff --git a/server/test/suites/server/struct_with_union.h b/server/test/suites/server/struct_with_union.h new file mode 100644 index 000000000..53669f4bf --- /dev/null +++ b/server/test/suites/server/struct_with_union.h @@ -0,0 +1,17 @@ +#ifndef SIMPLE_TEST_PROJECT_STRUCT_WITH_UNION_H +#define SIMPLE_TEST_PROJECT_STRUCT_WITH_UNION_H + +struct StructWithUnionOfUnnamedType { + union { + int x; + struct { + char c; + double d; + } ds; + long long *ptr; + } un; +}; + +struct StructWithUnionOfUnnamedType struct_with_union_of_unnamed_type_as_return_type(int a, int b); + +#endif // SIMPLE_TEST_PROJECT_STRUCT_WITH_UNION_H From 4d267ffc1cc1dc6aaa32d5432cd8da4e98e10822 Mon Sep 17 00:00:00 2001 From: IDKWNTCMF Date: Fri, 21 Jul 2023 13:45:18 +0300 Subject: [PATCH 6/7] Some fixes --- server/test/framework/Server_Tests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/test/framework/Server_Tests.cpp b/server/test/framework/Server_Tests.cpp index bfcb28d7a..9dc5b0072 100644 --- a/server/test/framework/Server_Tests.cpp +++ b/server/test/framework/Server_Tests.cpp @@ -461,7 +461,7 @@ namespace { testGen.tests.at(struct_with_union_c).methods) { if (methodName == "struct_with_union_of_unnamed_type_as_return_type") { checkTestCasePredicates( - testGen.tests.at(struct_with_union_c).methods.begin().value().testCases, + methodDescription.testCases, std::vector( {[] (const tests::Tests::MethodTestCase& testCase) { return stoi(testCase.paramValues[0].view->getEntryValue(nullptr)) < From 728170e1a201b5c13566afdd90defde2fda4e8a3 Mon Sep 17 00:00:00 2001 From: IDKWNTCMF Date: Fri, 21 Jul 2023 17:07:09 +0300 Subject: [PATCH 7/7] Change server test for this case --- server/test/framework/Server_Tests.cpp | 47 +++++++++++++++++++++----- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/server/test/framework/Server_Tests.cpp b/server/test/framework/Server_Tests.cpp index 9dc5b0072..2125cd134 100644 --- a/server/test/framework/Server_Tests.cpp +++ b/server/test/framework/Server_Tests.cpp @@ -904,14 +904,6 @@ namespace { checkSimpleUnions_C(testGen); } - TEST_F(Server_Test, Struct_With_Union) { - auto [testGen, status] = performFeatureFileTestsRequest(struct_with_union_c); - ASSERT_TRUE(status.ok()) << status.error_message(); - auto testFilePaths = CollectionUtils::getKeys(testGen.tests); - EXPECT_TRUE(!testFilePaths.empty()) << "Generated test files are missing."; - checkStructWithUnion_C(testGen); - } - TEST_F(Server_Test, Pointer_Parameters) { auto [testGen, status] = performFeatureFileTestsRequest(pointer_parameters_c); ASSERT_TRUE(status.ok()) << status.error_message(); @@ -2110,4 +2102,43 @@ namespace { StatusCountMap expectedStatusCountMap{ { testsgen::TEST_PASSED, 9 } }; testUtils::checkStatuses(resultsMap, tests); } + + TEST_F(Server_Test, Run_Tests_For_Struct_With_Union) { + fs::path struct_with_union_c = getTestFilePath("struct_with_union.c"); + auto request = testUtils::createFileRequest(projectName, suitePath, buildDirRelativePath, + srcPaths, struct_with_union_c, + GrpcUtils::UTBOT_AUTO_TARGET_PATH, true, false); + auto testGen = FileTestGen(*request, writer.get(), TESTMODE); + Status status = Server::TestsGenServiceImpl::ProcessBaseTestRequest(testGen, writer.get()); + ASSERT_TRUE(status.ok()) << status.error_message(); + EXPECT_GE(testUtils::getNumberOfTests(testGen.tests), 3); + checkStructWithUnion_C(testGen); + + fs::path testsDirPath = getTestFilePath("tests"); + + fs::path struct_with_union_test_cpp = Paths::sourcePathToTestPath( + utbot::ProjectContext(projectName, suitePath, testsDirPath, buildDirRelativePath, clientProjectPath), + struct_with_union_c); + auto testFilter = GrpcUtils::createTestFilterForFile(struct_with_union_test_cpp); + auto runRequest = testUtils::createCoverageAndResultsRequest( + projectName, suitePath, testsDirPath, buildDirRelativePath, std::move(testFilter)); + + static auto coverageAndResultsWriter = + std::make_unique(nullptr); + CoverageAndResultsGenerator coverageGenerator{ runRequest.get(), + coverageAndResultsWriter.get() }; + utbot::SettingsContext settingsContext{ + true, false, 45, 0, false, false, ErrorMode::FAILING, false + }; + coverageGenerator.generate(false, settingsContext); + + EXPECT_FALSE(coverageGenerator.hasExceptions()); + ASSERT_TRUE(coverageGenerator.getCoverageMap().empty()); + + auto resultsMap = coverageGenerator.getTestResultMap(); + auto tests = coverageGenerator.getTestsToLaunch(); + + StatusCountMap expectedStatusCountMap{ { testsgen::TEST_PASSED, 3 } }; + testUtils::checkStatuses(resultsMap, tests); + } }