diff --git a/server/src/Tests.h b/server/src/Tests.h index 72269a4ba..9dac51cc2 100644 --- a/server/src/Tests.h +++ b/server/src/Tests.h @@ -372,6 +372,18 @@ namespace tests { this->underscoredName() : this->name; } + + [[nodiscard]] std::string getFunctionParamDecl() const { + if (type.isTwoDimensionalPointer() && types::TypesHandler::isVoid(type.baseTypeObj())) { + std::string qualifier = type.isConstQualified() ? "const " : ""; + return StringUtils::stringFormat("(%svoid **) %s", qualifier, name); + } else if (type.isRValueReference()) { + return "std::move(" + name + ")"; + } else if (type.maybeJustPointer() && !type.isFilePointer() ) { + return "&" + name; + } + return name; + } }; struct TestCaseParamValue { diff --git a/server/src/printers/Printer.cpp b/server/src/printers/Printer.cpp index 4d2b5a3b9..baa05b72f 100644 --- a/server/src/printers/Printer.cpp +++ b/server/src/printers/Printer.cpp @@ -281,9 +281,7 @@ namespace printer { strTabIf(needTabs); std::vector parameters; for (const auto ¶m : method.params) { - std::string maybeAmpersand = - param.type.maybeJustPointer() && !param.type.isFilePointer() ? "&" : ""; - parameters.push_back(maybeAmpersand + param.name); + parameters.push_back(param.getFunctionParamDecl()); } auto classObjName = method.getClassName(); return strFunctionCall(method.name, parameters, end, classObjName, needTabs, diff --git a/server/src/printers/Printer.h b/server/src/printers/Printer.h index 72e50c331..c2eae834e 100644 --- a/server/src/printers/Printer.h +++ b/server/src/printers/Printer.h @@ -221,7 +221,7 @@ namespace printer { void writeAccessPrivateMacros(types::TypesHandler const *typesHandler, const Tests &tests, bool onlyChangeable); void genStubForStructFunctionPointer(const std::string &structName, - const types::Field &ieldName, + const types::Field &fieldName, const std::string &stubName); void genStubForStructFunctionPointerArray(const std::string &structName, diff --git a/server/src/printers/TestsPrinter.cpp b/server/src/printers/TestsPrinter.cpp index 2d3a14937..b3646cb22 100644 --- a/server/src/printers/TestsPrinter.cpp +++ b/server/src/printers/TestsPrinter.cpp @@ -709,16 +709,7 @@ TestsPrinter::methodParametersListVerbose(const Tests::MethodDescription &method const Tests::MethodTestCase &testCase) { std::vector args; for (const auto ¶m : methodDescription.params) { - if (param.type.isTwoDimensionalPointer() && - types::TypesHandler::isVoid(param.type.baseTypeObj())) { - std::string qualifier = Printer::getConstQualifier(param.type); - std::string arg = StringUtils::stringFormat("(%svoid **) %s", qualifier, param.name); - args.push_back(arg); - } else { - std::string maybeAmpersand = - param.type.maybeJustPointer() && !param.type.isFilePointer() ? "&" : ""; - args.push_back(maybeAmpersand + param.name); - } + args.push_back(param.getFunctionParamDecl()); } return args; } diff --git a/server/src/types/Types.cpp b/server/src/types/Types.cpp index 8ad5d6ab3..136fe3b83 100644 --- a/server/src/types/Types.cpp +++ b/server/src/types/Types.cpp @@ -248,6 +248,10 @@ bool types::Type::isLValueReference() const { return isSimple() && dynamic_cast(mKinds.front().get())->isLValue(); } +bool types::Type::isRValueReference() const { + return isSimple() && dynamic_cast(mKinds.front().get())->isRValue(); +} + bool types::Type::isConstQualified() const { return isSimple() && dynamic_cast(mKinds.front().get())->isConstQualified(); } diff --git a/server/src/types/Types.h b/server/src/types/Types.h index 05f3020e3..9dd9f3386 100644 --- a/server/src/types/Types.h +++ b/server/src/types/Types.h @@ -150,6 +150,12 @@ namespace types { */ [[nodiscard]] bool isLValueReference() const; + /** + * Checks whether given type is an rvalue reference type. + * @return true if type is rvalue reference, false otherwise. + */ + [[nodiscard]] bool isRValueReference() const; + /** * Checks whether given type is an const qualified. * @return true if type is const, false otherwise. diff --git a/server/test/framework/Syntax_Tests.cpp b/server/test/framework/Syntax_Tests.cpp index 831a0a057..b54881e44 100644 --- a/server/test/framework/Syntax_Tests.cpp +++ b/server/test/framework/Syntax_Tests.cpp @@ -54,10 +54,11 @@ namespace { fs::path inner_unnamed_c = getTestFilePath("inner_unnamed.c"); fs::path array_sort_c = getTestFilePath("array_sort.c"); fs::path stubs_c = getTestFilePath("stubs.c"); - fs::path namespace_cpp = getTestFilePath("namespace.cpp"); fs::path input_output_c = getTestFilePath("input_output.c"); fs::path file_c = getTestFilePath("file.c"); fs::path bitfields_c = getTestFilePath("bitfields.c"); + fs::path namespace_cpp = getTestFilePath("namespace.cpp"); + fs::path rvalue_reference_cpp = getTestFilePath("function_with_rvalue_params.cpp"); void SetUp() override { clearEnv(CompilationUtils::CompilerName::CLANG); @@ -2779,7 +2780,7 @@ namespace { ); } - TEST_F(Syntax_Test, multi_union) { + TEST_F(Syntax_Test, multi_union_cpp) { auto [testGen, status] = createTestForFunction(namespace_cpp, 38); ASSERT_TRUE(status.ok()) << status.error_message(); @@ -2798,6 +2799,146 @@ namespace { ); } + TEST_F(Syntax_Test, multiple_rvalue_params) { + auto [testGen, status] = createTestForFunction(rvalue_reference_cpp, 9); + + ASSERT_TRUE(status.ok()) << status.error_message(); + + testUtils::checkMinNumberOfTests(testGen.tests.at(rvalue_reference_cpp).methods.begin().value().testCases, 2); + + checkTestCasePredicates( + testGen.tests.at(rvalue_reference_cpp).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)); + }, + [] (const tests::Tests::MethodTestCase& testCase) { + return stoi(testCase.paramValues[0].view->getEntryValue(nullptr)) <= + stoi(testCase.paramValues[1].view->getEntryValue(nullptr)); + } + }) + ); + + checkTestCasePredicates( + testGen.tests.at(rvalue_reference_cpp).methods.begin().value().testCases, + std::vector( + { + [] (const tests::Tests::MethodTestCase& testCase) { + return 2 * stoi(testCase.paramValues[0].view->getEntryValue(nullptr)) == + stoi(testCase.returnValue.view->getEntryValue(nullptr)); + }, + [] (const tests::Tests::MethodTestCase& testCase) { + return 2 * stoi(testCase.paramValues[1].view->getEntryValue(nullptr)) == + stoi(testCase.returnValue.view->getEntryValue(nullptr)); + } + }) + ); + + } + + TEST_F(Syntax_Test, const_rvalue_reference) { + auto [testGen, status] = createTestForFunction(rvalue_reference_cpp, 17); + + ASSERT_TRUE(status.ok()) << status.error_message(); + + testUtils::checkMinNumberOfTests(testGen.tests.at(rvalue_reference_cpp).methods.begin().value().testCases, 3); + + checkTestCasePredicates( + testGen.tests.at(rvalue_reference_cpp).methods.begin().value().testCases, + std::vector( + { + [] (const tests::Tests::MethodTestCase& testCase) { + return stoi(testCase.returnValue.view->getEntryValue(nullptr)) == 0; + }, + [] (const tests::Tests::MethodTestCase& testCase) { + return stoi(testCase.returnValue.view->getEntryValue(nullptr)) == 1; + }, + [] (const tests::Tests::MethodTestCase& testCase) { + return stoi(testCase.returnValue.view->getEntryValue(nullptr)) == 2; + } + }) + ); + + checkTestCasePredicates( + testGen.tests.at(rvalue_reference_cpp).methods.begin().value().testCases, + std::vector( + { + [] (const tests::Tests::MethodTestCase& testCase) { + return stoi(testCase.paramValues.front().view->getEntryValue(nullptr)) % 3 == 0; + }, + [] (const tests::Tests::MethodTestCase& testCase) { + return stoi(testCase.paramValues.front().view->getEntryValue(nullptr)) % 3 == 1; + }, + [] (const tests::Tests::MethodTestCase& testCase) { + return stoi(testCase.paramValues.front().view->getEntryValue(nullptr)) % 3 == 2; + } + }) + ); + } + + TEST_F(Syntax_Test, return_and_get_params) { + auto [testGen, status] = createTestForFunction(rvalue_reference_cpp, 28); + + ASSERT_TRUE(status.ok()) << status.error_message(); + + testUtils::checkMinNumberOfTests(testGen.tests.at(rvalue_reference_cpp).methods.begin().value().testCases, 3); + + checkTestCasePredicates( + testGen.tests.at(rvalue_reference_cpp).methods.begin().value().testCases, + std::vector( + { + [] (const tests::Tests::MethodTestCase& testCase) { + return stoi(testCase.paramValues[0].view->getEntryValue(nullptr)) % 5 == 0; + }, + [] (const tests::Tests::MethodTestCase& testCase) { + return stoi(testCase.paramValues[1].view->getEntryValue(nullptr)) % 5 == 0; + } + }) + ); + + checkTestCasePredicates( + testGen.tests.at(rvalue_reference_cpp).methods.begin().value().testCases, + std::vector( + { + [] (const tests::Tests::MethodTestCase& testCase) { + return stoi(testCase.paramValues[0].view->getEntryValue(nullptr)) == + stoi(testCase.returnValue.view->getEntryValue(nullptr)); + }, + [] (const tests::Tests::MethodTestCase& testCase) { + return stoi(testCase.paramValues[1].view->getEntryValue(nullptr)) == + stoi(testCase.returnValue.view->getEntryValue(nullptr)); + }, + [] (const tests::Tests::MethodTestCase& testCase) { + return stoi(testCase.paramValues[0].view->getEntryValue(nullptr)) + stoi(testCase.paramValues[1].view->getEntryValue(nullptr))== + stoi(testCase.returnValue.view->getEntryValue(nullptr)); + } + }) + ); + } + + TEST_F(Syntax_Test, rvalue_struct_param) { + auto [testGen, status] = createTestForFunction(rvalue_reference_cpp, 38); + + ASSERT_TRUE(status.ok()) << status.error_message(); + + testUtils::checkMinNumberOfTests(testGen.tests.at(rvalue_reference_cpp).methods.begin().value().testCases, 4); + + checkTestCasePredicates( + testGen.tests.at(rvalue_reference_cpp).methods.begin().value().testCases, + std::vector( + { + [] (const tests::Tests::MethodTestCase& testCase) { + return stoi(testCase.returnValue.view->getEntryValue(nullptr)) == 1; + }, + [] (const tests::Tests::MethodTestCase& testCase) { + return stoi(testCase.returnValue.view->getEntryValue(nullptr)) == 2; + } + }) + ); + } + TEST_F(Syntax_Test, simple_getc) { auto [testGen, status] = createTestForFunction(input_output_c, 4); diff --git a/server/test/suites/syntax/CMakeLists.txt b/server/test/suites/syntax/CMakeLists.txt index 0a352cd4a..b1f5ef9be 100644 --- a/server/test/suites/syntax/CMakeLists.txt +++ b/server/test/suites/syntax/CMakeLists.txt @@ -40,4 +40,5 @@ add_executable(syntax1 namespace.cpp input_output.c file.c - bitfields.c) + bitfields.c + function_with_rvalue_params.cpp) diff --git a/server/test/suites/syntax/function_with_rvalue_params.cpp b/server/test/suites/syntax/function_with_rvalue_params.cpp new file mode 100644 index 000000000..b49e52468 --- /dev/null +++ b/server/test/suites/syntax/function_with_rvalue_params.cpp @@ -0,0 +1,59 @@ +#include +#include "function_with_rvalue_params.h" + +int double_max(int && first, int && second) { + if (first > second) { + return 2 * first; + } else { + return 2 * second; + } +} + +int remainder(const int && value) { + if (value % 3 == 0) { + return 0; + } else if (value % 3 == 1) { + return 1; + } else { + return 2; + } +} + +int && return_and_get_rvalue_reference(int && first, int && second) { + if (first % 5 == 0) { + return std::move(first); + } else if (second % 5 == 0) { + return std::move(second); + } else { + return std::move(first + second); + } +} + +int get_rvalue_custom_struct_as_param(Closet && closet) { + if (closet.height > 5 && closet.width > 5 && closet.length > 5) { + closet.height /= 5; + closet.volume /= 5; + closet.width /= 5; + return 1; + } else { + closet.width = 5; + closet.height = 5; + closet.length = 5; + closet.volume = 125; + return 2; + } +} + +Closet::Closet() { + length = 1.5; + width = 0.5; + height = 2.5; + volume = height * width * length; +} + +Closet::Closet(double length_, double width_, double height_, double volume_) { + length = length_; + width = width_; + height = height_; + volume = volume_; +} diff --git a/server/test/suites/syntax/function_with_rvalue_params.h b/server/test/suites/syntax/function_with_rvalue_params.h new file mode 100644 index 000000000..8229dd613 --- /dev/null +++ b/server/test/suites/syntax/function_with_rvalue_params.h @@ -0,0 +1,22 @@ +#ifndef UNITTESTBOT_FUNCTION_WITH_RVALUE_PARAMS_H +#define UNITTESTBOT_FUNCTION_WITH_RVALUE_PARAMS_H + +struct Closet { + double length; + double width; + double height; + double volume; + + Closet(); + Closet(double length_, double width_, double height_, double volume_); +}; + +int double_max(int && first, int && second); + +int remainder(const int && value); + +int && return_and_get_rvalue_reference(int && first, int && second); + +int get_rvalue_custom_struct_as_param(Closet && closet); + +#endif // UNITTESTBOT_FUNCTION_WITH_RVALUE_PARAMS_H