diff --git a/server/src/FeaturesFilter.cpp b/server/src/FeaturesFilter.cpp index d6b6721d..b9dc79d5 100644 --- a/server/src/FeaturesFilter.cpp +++ b/server/src/FeaturesFilter.cpp @@ -34,23 +34,24 @@ void FeaturesFilter::filter(utbot::SettingsContext const &settingsContext, size_t erased = CollectionUtils::erase_if(tests.methods, [&](const tests::Tests::MethodDescription &method) { - auto returnTypeSupport = typesHandler.isSupportedType(method.returnType, types::TypeUsage::RETURN); - updateIfNotCompleteType(returnTypeSupport, method.hasIncompleteReturnType, - "Method has incomplete return type"); - if (!returnTypeSupport.isSupported) { - unsupportedStatistics[returnTypeSupport.info]++; - std::stringstream message; - message << "Function '" << method.name << "' was skipped, as return type '" - << method.returnType.typeName() - << "' is not fully supported: " << returnTypeSupport.info; - LOG_S(WARNING) << message.str(); - tests.commentBlocks.push_back(message.str()); - return true; + if (!method.hasPointerToIncompleteReturnType) { + auto returnTypeSupport = + typesHandler.isSupportedType(method.returnType, types::TypeUsage::RETURN); + if (!returnTypeSupport.isSupported) { + unsupportedStatistics[returnTypeSupport.info]++; + std::stringstream message; + message << "Function '" << method.name << "' was skipped, as return type '" + << method.returnType.typeName() + << "' is not fully supported: " << returnTypeSupport.info; + LOG_S(WARNING) << message.str(); + tests.commentBlocks.push_back(message.str()); + return true; + } } for (const auto ¶m: method.params) { auto paramTypeSupport = typesHandler.isSupportedType(param.type, types::TypeUsage::PARAMETER); - updateIfNotCompleteType(paramTypeSupport, param.hasIncompleteType, + updateIfNotCompleteType(paramTypeSupport, param.isPointerToIncomplete, "Parameter has incomplete type"); if (!paramTypeSupport.isSupported) { unsupportedStatistics[paramTypeSupport.info]++; diff --git a/server/src/Tests.cpp b/server/src/Tests.cpp index c6fb46ff..c67920a6 100644 --- a/server/src/Tests.cpp +++ b/server/src/Tests.cpp @@ -858,7 +858,8 @@ KTestObjectParser::parseTestCaseParams(const UTBotKTest &ktest, //processSymbolicStdin(testCaseDescription, rawKleeParams); processStubParamValue(testCaseDescription, methodNameToReturnTypeMap, rawKleeParams); - if (!types::TypesHandler::skipTypeInReturn(methodDescription.returnType)) { + if (!types::TypesHandler::skipTypeInReturn(methodDescription.returnType) && + !methodDescription.hasPointerToIncompleteReturnType) { const auto kleeResParam = getKleeParamOrThrow(rawKleeParams, KleeUtils::RESULT_VARIABLE_NAME); auto paramType = methodDescription.returnType.maybeReturnArray() ? methodDescription.returnType : methodDescription.returnType.baseTypeObj(); diff --git a/server/src/Tests.h b/server/src/Tests.h index 4d2fda9d..7cb9c8da 100644 --- a/server/src/Tests.h +++ b/server/src/Tests.h @@ -305,14 +305,14 @@ namespace tests { string name; std::optional alignment; - bool hasIncompleteType = false; + bool isPointerToIncomplete = false; MethodParam(types::Type type, string name, std::optional alignment, - bool hasIncompleteType = false) + bool isPointerToIncomplete_ = false) : type(std::move(type)), name(std::move(name)), alignment(std::move(alignment)), - hasIncompleteType(hasIncompleteType) { + isPointerToIncomplete(isPointerToIncomplete_) { } @@ -412,7 +412,7 @@ namespace tests { std::string paramsString; types::Type returnType; - bool hasIncompleteReturnType = false; + bool hasPointerToIncompleteReturnType = false; std::optional sourceBody; Modifiers modifiers; diff --git a/server/src/clang-utils/ClangUtils.cpp b/server/src/clang-utils/ClangUtils.cpp index 0f23bd00..912db063 100644 --- a/server/src/clang-utils/ClangUtils.cpp +++ b/server/src/clang-utils/ClangUtils.cpp @@ -16,7 +16,7 @@ namespace ClangUtils { return false; } - bool isIncomplete(clang::QualType type) { + bool isPointerToIncomplete(clang::QualType type) { clang::QualType canonicalType = type.getCanonicalType(); if (auto const *pType = canonicalType.getTypePtrOrNull()) { auto pointeeType = pType->getPointeeType(); diff --git a/server/src/clang-utils/ClangUtils.h b/server/src/clang-utils/ClangUtils.h index 19a980cb..d9044419 100644 --- a/server/src/clang-utils/ClangUtils.h +++ b/server/src/clang-utils/ClangUtils.h @@ -8,7 +8,7 @@ #include namespace ClangUtils { - bool isIncomplete(clang::QualType type); + bool isPointerToIncomplete(clang::QualType type); }; diff --git a/server/src/fetchers/FunctionDeclsMatchCallback.cpp b/server/src/fetchers/FunctionDeclsMatchCallback.cpp index 10c6faba..9b47f6cc 100644 --- a/server/src/fetchers/FunctionDeclsMatchCallback.cpp +++ b/server/src/fetchers/FunctionDeclsMatchCallback.cpp @@ -69,7 +69,8 @@ void FunctionDeclsMatchCallback::run(const MatchFinder::MatchResult &Result) { std::nullopt }; } methodDescription.returnType = ParamsHandler::getType(realReturnType, realReturnType, sourceManager); - methodDescription.hasIncompleteReturnType = ClangUtils::isIncomplete(realReturnType); + methodDescription.hasPointerToIncompleteReturnType = + ClangUtils::isPointerToIncomplete(realReturnType); if (toResolveReturnTypes) { typesResolver.resolve(realReturnType); } @@ -109,8 +110,9 @@ void FunctionDeclsMatchCallback::run(const MatchFinder::MatchResult &Result) { addFunctionPointer(methodDescription.functionPointers, declParam->getFunctionType(), declParam->getType(), name, sourceManager, paramType); auto alignment = AlignmentFetcher::fetch(defParam); - bool hasIncompleteType = ClangUtils::isIncomplete(defParam->getType()); - methodDescription.params.emplace_back(paramType, name, alignment, hasIncompleteType); + bool isPointerToIncomplete = ClangUtils::isPointerToIncomplete(defParam->getType()); + methodDescription.params.emplace_back(paramType, name, alignment, + isPointerToIncomplete); } if (CollectionUtils::contains(methods[sourceFilePath], methodDescription)) { LOG_S(ERROR) << "Method " << methodDescription.name << " from " << sourceFilePath diff --git a/server/src/fetchers/GlobalVariableUsageMatchCallback.cpp b/server/src/fetchers/GlobalVariableUsageMatchCallback.cpp index 32421a44..4f0e70c4 100644 --- a/server/src/fetchers/GlobalVariableUsageMatchCallback.cpp +++ b/server/src/fetchers/GlobalVariableUsageMatchCallback.cpp @@ -38,7 +38,7 @@ void GlobalVariableUsageMatchCallback::checkUsage(const MatchFinder::MatchResult LOG_S(MAX) << "Variable \"" << name << "\" was skipped - it is being blacklisted."; return; } - if (ClangUtils::isIncomplete(pVarDecl->getType())) { + if (ClangUtils::isPointerToIncomplete(pVarDecl->getType())) { LOG_S(MAX) << "Variable \"" << name << "\" was skipped - it's type has no definition in current " "translation unit."; diff --git a/server/src/printers/KleePrinter.cpp b/server/src/printers/KleePrinter.cpp index af494962..03192bbe 100644 --- a/server/src/printers/KleePrinter.cpp +++ b/server/src/printers/KleePrinter.cpp @@ -373,24 +373,29 @@ void KleePrinter::makeBracketsForStrPredicate(const std::optional &inf } -void KleePrinter::genReturnDeclaration(const Tests::MethodDescription &testMethod, const std::optional &predicateInfo) { +void KleePrinter::genReturnDeclaration(const Tests::MethodDescription &testMethod, + const std::optional &predicateInfo) { // If return type is a pointer, we compare values that are stored at this pointers, // not the pointers themselves - Type returnType = types::TypesHandler::isVoid(testMethod.returnType.baseTypeObj()) - ? Type::minimalScalarType() - : testMethod.returnType; - bool maybeArray = returnType.maybeReturnArray(); - bool isPointer = testMethod.returnType.isObjectPointer(); - strDeclareVar(returnType.baseType(), KleeUtils::RESULT_VARIABLE_NAME, std::nullopt, std::nullopt, false); - makeBracketsForStrPredicate(predicateInfo); - if (maybeArray) { - size_t size = types::TypesHandler::getElementsNumberInPointerOneDim(PointerUsage::RETURN); - ss << "[" << size << "]"; - } - ss << SCNL; - strKleeMakeSymbolic(KleeUtils::RESULT_VARIABLE_NAME, - !maybeArray && !(predicateInfo.has_value() && predicateInfo->type == testsgen::STRING)); - if (isPointer) { + if (!testMethod.hasPointerToIncompleteReturnType) { + Type returnType = types::TypesHandler::isVoid(testMethod.returnType.baseTypeObj()) + ? Type::minimalScalarType() + : testMethod.returnType; + bool maybeArray = returnType.maybeReturnArray(); + strDeclareVar(returnType.baseType(), KleeUtils::RESULT_VARIABLE_NAME, std::nullopt, + std::nullopt, false); + makeBracketsForStrPredicate(predicateInfo); + if (maybeArray) { + size_t size = + types::TypesHandler::getElementsNumberInPointerOneDim(PointerUsage::RETURN); + ss << "[" << size << "]"; + } + ss << SCNL; + strKleeMakeSymbolic( + KleeUtils::RESULT_VARIABLE_NAME, + !maybeArray && !(predicateInfo.has_value() && predicateInfo->type == testsgen::STRING)); + } + if (testMethod.returnType.isObjectPointer()) { strDeclareVar("int", KleeUtils::NOT_NULL_VARIABLE_NAME); strKleeMakeSymbolic(KleeUtils::NOT_NULL_VARIABLE_NAME, true); } diff --git a/server/src/printers/TestsPrinter.cpp b/server/src/printers/TestsPrinter.cpp index 586ece99..93b27e45 100644 --- a/server/src/printers/TestsPrinter.cpp +++ b/server/src/printers/TestsPrinter.cpp @@ -382,7 +382,10 @@ void TestsPrinter::verboseOutputVariable(const Tests::MethodDescription &methodD void TestsPrinter::verboseFunctionCall(const Tests::MethodDescription &methodDescription, const Tests::MethodTestCase &testCase) { std::string baseReturnType = types::TypesHandler::cBoolToCpp(methodDescription.returnType.baseType()); - types::Type expectedType = typesHandler->getReturnTypeToCheck(methodDescription.returnType); + types::Type expectedType = + methodDescription.hasPointerToIncompleteReturnType + ? methodDescription.returnType + : typesHandler->getReturnTypeToCheck(methodDescription.returnType); if (methodDescription.returnType.maybeReturnArray()) { expectedType = methodDescription.returnType.arrayClone(types::PointerUsage::RETURN); } @@ -519,7 +522,9 @@ void TestsPrinter::parametrizedAsserts(const Tests::MethodDescription &methodDes const std::optional& predicateInfo) { auto visitor = visitor::ParametrizedAssertsVisitor(typesHandler, this, predicateInfo, testCase.isError()); visitor.visit(methodDescription, testCase); - globalParamsAsserts(methodDescription, testCase); + if (!testCase.isError()) { + globalParamsAsserts(methodDescription, testCase); + } classAsserts(methodDescription, testCase); changeableParamsAsserts(methodDescription, testCase); } @@ -577,11 +582,12 @@ string TestsPrinter::constrVisitorFunctionCall(const Tests::MethodDescription &m std::optional castType; if (types::TypesHandler::skipTypeInReturn(methodDescription.returnType.baseTypeObj()) && methodDescription.returnType.isObjectPointer()) { - castType = types::Type::minimalScalarPointerType(); + castType = types::Type::minimalScalarPointerType(methodDescription.returnType.getDimension()); } auto classObjName = methodDescription.getClassName(); size_t returnPointersCount = 0; - if (testCase.returnValueView && testCase.returnValueView->getEntryValue() != PrinterUtils::C_NULL) { + if (!methodDescription.hasPointerToIncompleteReturnType && + testCase.returnValueView->getEntryValue() != PrinterUtils::C_NULL) { returnPointersCount = methodDescription.returnType.countReturnPointers(true); } return constrFunctionCall(methodDescription.name, methodArgs, "", classObjName, false, returnPointersCount, diff --git a/server/src/visitors/AbstractValueViewVisitor.h b/server/src/visitors/AbstractValueViewVisitor.h index 898ef247..1e928f95 100644 --- a/server/src/visitors/AbstractValueViewVisitor.h +++ b/server/src/visitors/AbstractValueViewVisitor.h @@ -14,7 +14,7 @@ namespace visitor { protected: types::TypesHandler const * const typesHandler; types::PointerUsage usage; - size_t additionalPointersCount; + size_t additionalPointersCount = 0; bool inUnion = false; public: explicit AbstractValueViewVisitor(types::TypesHandler const *typesHandler, diff --git a/server/src/visitors/AssertsVisitor.h b/server/src/visitors/AssertsVisitor.h index 2277dcd3..14fd9347 100644 --- a/server/src/visitors/AssertsVisitor.h +++ b/server/src/visitors/AssertsVisitor.h @@ -30,8 +30,10 @@ namespace visitor { [[nodiscard]] std::string getDecorateActualVarName(const string& access); - FunctionSignature changeSignatureToNullCheck(const FunctionSignature& signature, const types::Type& type, - const tests::AbstractValueView *view, const string &access); + FunctionSignature changeSignatureToNullCheck(const FunctionSignature &signature, + const types::Type &type, + const tests::AbstractValueView *view, + const string &access); public: explicit AssertsVisitor(const types::TypesHandler *typesHandler, diff --git a/server/src/visitors/KleeAssumeReturnValueVisitor.cpp b/server/src/visitors/KleeAssumeReturnValueVisitor.cpp index 729d9dbf..0ae11461 100644 --- a/server/src/visitors/KleeAssumeReturnValueVisitor.cpp +++ b/server/src/visitors/KleeAssumeReturnValueVisitor.cpp @@ -74,7 +74,6 @@ namespace visitor { const string &access, int depth) { if (depth == 0) { - kleeAssumeWithNullCheck("", false); AbstractValueViewVisitor::visitStruct(type, KleeUtils::TEMP_VARIABLE_NAME, view, PrinterUtils::DEFAULT_ACCESS, depth); } else { @@ -87,9 +86,6 @@ namespace visitor { const tests::AbstractValueView *view, const string &access, int depth) { - if (depth == 0) { - kleeAssumeWithNullCheck("", false); - } AbstractValueViewVisitor::visitUnion(type, name, view, access, depth); } @@ -129,21 +125,9 @@ namespace visitor { printer->closeBrackets(sizes.size()); } - void KleeAssumeReturnValueVisitor::kleeAssumeWithNullCheck(const string& assumption, bool useBasicAssumeIfNotPointer) { - if (!useBasicAssumeIfNotPointer && additionalPointersCount == 0) { - return; - } - if (additionalPointersCount > 0) { - auto notNullAssumptionCheck = KleeUtils::NOT_NULL_VARIABLE_NAME + " == 1"; - if (assumption.empty()) { - kleeAssume(notNullAssumptionCheck); - } else { - kleeAssume(notNullAssumptionCheck + " & " + assumption); - } - } else { - kleeAssume(assumption); - } - } + void KleeAssumeReturnValueVisitor::kleeAssumeWithNullCheck(const string& assumption) { + kleeAssume(assumption); + } types::Type KleeAssumeReturnValueVisitor::getActualTmpVarType(const types::Type &type) { if (types::TypesHandler::isVoid(type.baseTypeObj())) { @@ -160,6 +144,7 @@ namespace visitor { if (additionalPointersCount > 0) { printer->ss << printer->TAB_N() << "if (" << KleeUtils::TEMP_VARIABLE_NAME << " != " << PrinterUtils::C_NULL << ")" << printer->LB(); + kleeAssume(KleeUtils::NOT_NULL_VARIABLE_NAME + " == 1"); } } diff --git a/server/src/visitors/KleeAssumeReturnValueVisitor.h b/server/src/visitors/KleeAssumeReturnValueVisitor.h index f155bee5..c18d704b 100644 --- a/server/src/visitors/KleeAssumeReturnValueVisitor.h +++ b/server/src/visitors/KleeAssumeReturnValueVisitor.h @@ -55,7 +55,7 @@ namespace visitor { void checkNotNullAfter(); - void kleeAssumeWithNullCheck(const string& assumption, bool useBasicAssumeIfNotPointer = true); + void kleeAssumeWithNullCheck(const string& assumption); static types::Type getActualTmpVarType(const types::Type &type); }; diff --git a/server/src/visitors/ParametrizedAssertsVisitor.cpp b/server/src/visitors/ParametrizedAssertsVisitor.cpp index 7296616a..d6718eb9 100644 --- a/server/src/visitors/ParametrizedAssertsVisitor.cpp +++ b/server/src/visitors/ParametrizedAssertsVisitor.cpp @@ -22,17 +22,18 @@ namespace visitor { functionCall = printer->constrVisitorFunctionCall(methodDescription, testCase, false); if (testCase.returnValueView->getEntryValue() == PrinterUtils::C_NULL) { - additionalPointersCount = methodDescription.returnType.countReturnPointers(true); printer->writeCodeLine( - StringUtils::stringFormat("EXPECT_TRUE(%s)", - PrinterUtils::getEqualString(functionCall, PrinterUtils::C_NULL))); + StringUtils::stringFormat("EXPECT_EQ(%s, %s)", PrinterUtils::C_NULL, functionCall)); + return; + } + if (methodDescription.hasPointerToIncompleteReturnType) { + printer->writeCodeLine(StringUtils::stringFormat( + "EXPECT_NE((%s) %s, %s)", methodDescription.returnType.typeName(), + PrinterUtils::C_NULL, functionCall)); return; - } else { - additionalPointersCount = 0; } visitAny(returnType, "", testCase.returnValueView.get(), PrinterUtils::DEFAULT_ACCESS, 0); functionCall = {}; - additionalPointersCount = 0; } void ParametrizedAssertsVisitor::visitArray(const types::Type &type, @@ -50,8 +51,9 @@ namespace visitor { printer->strDeclareVar( printer::Printer::getConstQualifier(type) + type.usedType(), PrinterUtils::ACTUAL, functionCall, std::nullopt, true, additionalPointersCount); - printer->strDeclareArrayVar(type, PrinterUtils::fillVarName(access, PrinterUtils::EXPECTED), usage, - view->getEntryValue(), true); + printer->strDeclareArrayVar( + type, PrinterUtils::fillVarName(access, PrinterUtils::EXPECTED), usage, + view->getEntryValue(), std::nullopt, true); } } else { return AbstractValueViewVisitor::visitAny(type.baseTypeObj(), name, view, access, depth); @@ -77,7 +79,8 @@ namespace visitor { int depth) { if (depth == 0) { printer->strDeclareVar(printer::Printer::getConstQualifier(type) + type.usedType(), - PrinterUtils::ACTUAL, functionCall, std::nullopt, true, additionalPointersCount); + PrinterUtils::ACTUAL, functionCall, std::nullopt, true, + additionalPointersCount); printer->strDeclareVar(type.typeName(), PrinterUtils::fillVarName(access, PrinterUtils::EXPECTED), view->getEntryValue()); } AbstractValueViewVisitor::visitStruct(type, name, view, access, depth); @@ -90,7 +93,8 @@ namespace visitor { int depth) { if (depth == 0) { printer->strDeclareVar(printer::Printer::getConstQualifier(type) + type.usedType(), - PrinterUtils::ACTUAL, functionCall, std::nullopt, true, additionalPointersCount); + PrinterUtils::ACTUAL, functionCall, std::nullopt, true, + additionalPointersCount); printer->strDeclareVar(type.typeName(), PrinterUtils::EXPECTED, view->getEntryValue()); } AbstractValueViewVisitor::visitUnion(type, name, view, access, depth); @@ -111,7 +115,6 @@ namespace visitor { const auto >estMacro = predicateMapping.at(predicate); auto signature = processExpect(type, gtestMacro, {view->getEntryValue(), getDecorateActualVarName(access)}); - signature = changeSignatureToNullCheck(signature, type, view, access); printer->strFunctionCall(signature.name, signature.args, SCNL, std::nullopt, true, 0, std::nullopt, inUnion); } @@ -123,7 +126,6 @@ namespace visitor { auto signature = processExpect(type, gtestMacro, {getDecorateActualVarName(access), PrinterUtils::fillVarName(access, PrinterUtils::EXPECTED)}); - signature = changeSignatureToNullCheck(signature, type, view, access); printer->strFunctionCall(signature.name, signature.args, SCNL, std::nullopt, true, 0, std::nullopt, inUnion); } diff --git a/server/src/visitors/VerboseAssertsReturnValueVisitor.cpp b/server/src/visitors/VerboseAssertsReturnValueVisitor.cpp index adff7924..9cb0220c 100644 --- a/server/src/visitors/VerboseAssertsReturnValueVisitor.cpp +++ b/server/src/visitors/VerboseAssertsReturnValueVisitor.cpp @@ -18,7 +18,13 @@ namespace visitor { : methodDescription.returnType.baseTypeObj(); if (testCase.returnValueView->getEntryValue() == PrinterUtils::C_NULL) { additionalPointersCount = methodDescription.returnType.countReturnPointers(true); - printer->writeCodeLine(StringUtils::stringFormat("EXPECT_TRUE(%s" + PrinterUtils::EQ_OPERATOR + PrinterUtils::C_NULL + ")", PrinterUtils::ACTUAL)); + printer->writeCodeLine(StringUtils::stringFormat("EXPECT_EQ(%s, %s)", PrinterUtils::C_NULL, PrinterUtils::ACTUAL)); + return; + } + if (methodDescription.hasPointerToIncompleteReturnType) { + printer->writeCodeLine(StringUtils::stringFormat( + "EXPECT_NE((%s) %s, %s)", methodDescription.returnType.typeName(), + PrinterUtils::C_NULL, PrinterUtils::ACTUAL)); return; } additionalPointersCount = 0; diff --git a/server/test/framework/Syntax_Tests.cpp b/server/test/framework/Syntax_Tests.cpp index e37e3096..56c3f018 100644 --- a/server/test/framework/Syntax_Tests.cpp +++ b/server/test/framework/Syntax_Tests.cpp @@ -2009,7 +2009,7 @@ namespace { TEST_F(Syntax_Test, Return_Incomplete) { auto [testGen, status] = createTestForFunction(types_3_c, 109); - ASSERT_TRUE(status.error_code() == grpc::FAILED_PRECONDITION) << status.error_message(); + testUtils::checkMinNumberOfTests(testGen.tests.at(types_3_c).methods.begin().value().testCases, 1); } TEST_F(Syntax_Test, Pass_Forward_Decl) {