// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "testmodifyfunction.h" #include "testutil.h" #include #include #include #include #include #include #include #include #include using namespace Qt::StringLiterals; void TestModifyFunction::testRenameArgument_data() { QTest::addColumn("pattern"); QTest::newRow("fixed_string") << QByteArrayLiteral("method(int)"); QTest::newRow("regular_expression") << QByteArrayLiteral("^method.*"); } void TestModifyFunction::testRenameArgument() { QFETCH(QByteArray, pattern); const char cppCode[] = "\ struct A {\n\ void method(int=0);\n\ };\n"; const char xmlCode1[] = "\ \n\ \n\ \n\ )"; const QByteArray xmlCode = QByteArray(xmlCode1) + pattern + QByteArray(xmlCode2); QScopedPointer builder(TestUtil::parse(cppCode, xmlCode.constData(), false)); QVERIFY(builder); AbstractMetaClassList classes = builder->classes(); const auto classA = AbstractMetaClass::findClass(classes, "A"); const auto func = classA->findFunction("method"); QVERIFY(func); QCOMPARE(func->argumentName(1), u"otherArg"); } void TestModifyFunction::testOwnershipTransfer() { const char cppCode[] = "\ struct A {};\n\ struct B {\n\ virtual A* method();\n\ };\n"; const char xmlCode[] = "\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n"; QScopedPointer builder(TestUtil::parse(cppCode, xmlCode, false)); QVERIFY(builder); AbstractMetaClassList classes = builder->classes(); const auto classB = AbstractMetaClass::findClass(classes, "B"); const auto func = classB->findFunction("method"); QVERIFY(func); QCOMPARE(func->argumentTargetOwnership(func->ownerClass(), 0), TypeSystem::CppOwnership); } void TestModifyFunction::invalidateAfterUse() { const char cppCode[] = "\ struct A {\n\ virtual void call(int *a);\n\ };\n\ struct B : A {\n\ };\n\ struct C : B {\n\ virtual void call2(int *a);\n\ };\n\ struct D : C {\n\ virtual void call2(int *a);\n\ };\n\ struct E : D {\n\ };\n"; const char xmlCode[] = "\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n"; QScopedPointer builder(TestUtil::parse(cppCode, xmlCode, false, u"0.1"_s)); QVERIFY(builder); AbstractMetaClassList classes = builder->classes(); const auto classB = AbstractMetaClass::findClass(classes, "B"); auto func = classB->findFunction("call"); QCOMPARE(func->modifications().size(), 1); QCOMPARE(func->modifications().at(0).argument_mods().size(), 1); QVERIFY(func->modifications().at(0).argument_mods().at(0).resetAfterUse()); const auto classC = AbstractMetaClass::findClass(classes, "C"); QVERIFY(classC); func = classC->findFunction("call"); QCOMPARE(func->modifications().size(), 1); QCOMPARE(func->modifications().at(0).argument_mods().size(), 1); QVERIFY(func->modifications().at(0).argument_mods().at(0).resetAfterUse()); func = classC->findFunction("call2"); QCOMPARE(func->modifications().size(), 1); QCOMPARE(func->modifications().at(0).argument_mods().size(), 1); QVERIFY(func->modifications().at(0).argument_mods().at(0).resetAfterUse()); AbstractMetaClassCPtr classD = AbstractMetaClass::findClass(classes, "D"); QVERIFY(classD); func = classD->findFunction("call"); QCOMPARE(func->modifications().size(), 1); QCOMPARE(func->modifications().at(0).argument_mods().size(), 1); QVERIFY(func->modifications().at(0).argument_mods().at(0).resetAfterUse()); func = classD->findFunction("call2"); QCOMPARE(func->modifications().size(), 1); QCOMPARE(func->modifications().at(0).argument_mods().size(), 1); QVERIFY(func->modifications().at(0).argument_mods().at(0).resetAfterUse()); const auto classE = AbstractMetaClass::findClass(classes, "E"); QVERIFY(classE); func = classE->findFunction("call"); QVERIFY(func); QCOMPARE(func->modifications().size(), 1); QCOMPARE(func->modifications().at(0).argument_mods().size(), 1); QVERIFY(func->modifications().at(0).argument_mods().at(0).resetAfterUse()); func = classE->findFunction("call2"); QVERIFY(func); QCOMPARE(func->modifications().size(), 1); QCOMPARE(func->modifications().at(0).argument_mods().size(), 1); QVERIFY(func->modifications().at(0).argument_mods().at(0).resetAfterUse()); } void TestModifyFunction::testWithApiVersion() { const char cppCode[] = "\ struct A {};\n\ struct B {\n\ virtual A* method();\n\ virtual B* methodB();\n\ };\n"; const char xmlCode[] = "\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n"; QScopedPointer builder(TestUtil::parse(cppCode, xmlCode, false, u"0.1"_s)); QVERIFY(builder); AbstractMetaClassList classes = builder->classes(); const auto classB = AbstractMetaClass::findClass(classes, "B"); auto func = classB->findFunction("method"); auto returnOwnership = func->argumentTargetOwnership(func->ownerClass(), 0); QCOMPARE(returnOwnership, TypeSystem::CppOwnership); func = classB->findFunction("methodB"); returnOwnership = func->argumentTargetOwnership(func->ownerClass(), 0); QVERIFY(returnOwnership != TypeSystem::CppOwnership); } // Modifications on class/typesystem level are tested below // in testScopedModifications(). void TestModifyFunction::testAllowThread() { const char cppCode[] =R"CPP(\ struct A { void f1(); void f2(); void f3(); int getter1() const; int getter2() const; }; )CPP"; const char xmlCode[] = R"XML( )XML"; QScopedPointer builder(TestUtil::parse(cppCode, xmlCode, false, u"0.1"_s)); QVERIFY(builder); AbstractMetaClassList classes = builder->classes(); const auto classA = AbstractMetaClass::findClass(classes, "A"); QVERIFY(classA); // Nothing specified, true const auto f1 = classA->findFunction("f1"); QVERIFY(f1); QVERIFY(!f1->allowThread()); // 'auto' specified, should be false for nontrivial function const auto f2 = classA->findFunction("f2"); QVERIFY(f2); QVERIFY(f2->allowThread()); // 'no' specified, should be false const auto f3 = classA->findFunction("f3"); QVERIFY(f3); QVERIFY(!f3->allowThread()); // Nothing specified, should be false for simple getter const auto getter1 = classA->findFunction("getter1"); QVERIFY(getter1); QVERIFY(!getter1->allowThread()); // Forced to true simple getter const auto getter2 = classA->findFunction("getter2"); QVERIFY(getter2); QVERIFY(getter2->allowThread()); // Forced to true simple getter } void TestModifyFunction::testGlobalFunctionModification() { const char cppCode[] = "\ struct A {};\n\ void function(A* a = 0);\n"; const char xmlCode[] = "\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n"; QScopedPointer builder(TestUtil::parse(cppCode, xmlCode, false)); QVERIFY(builder); QCOMPARE(builder->globalFunctions().size(), 1); auto *td = TypeDatabase::instance(); FunctionModificationList mods = td->globalFunctionModifications({u"function(A*)"_s}); QCOMPARE(mods.size(), 1); const QList &argMods = mods.constFirst().argument_mods(); QCOMPARE(argMods.size(), 1); ArgumentModification argMod = argMods.constFirst(); QCOMPARE(argMod.replacedDefaultExpression(), u"A()"); QVERIFY(!builder->globalFunctions().isEmpty()); const auto func = builder->globalFunctions().constFirst(); QCOMPARE(func->arguments().size(), 1); const AbstractMetaArgument &arg = func->arguments().constFirst(); QCOMPARE(arg.type().cppSignature(), u"A *"); QCOMPARE(arg.originalDefaultValueExpression(), u"0"); QCOMPARE(arg.defaultValueExpression(), u"A()"); } // Tests modifications of exception handling and allow-thread // on various levels. void TestModifyFunction::testScopedModifications_data() { QTest::addColumn("cppCode"); QTest::addColumn("xmlCode"); QTest::addColumn("expectedGenerateUnspecified"); QTest::addColumn("expectedGenerateNonThrowing"); QTest::addColumn("expectedGenerateThrowing"); QTest::addColumn("expectedAllowThread"); const QByteArray cppCode = R"CPP( struct Base { }; struct A : public Base { void unspecified(); void nonThrowing() noexcept; # if __cplusplus >= 201703L // C++ 17 void throwing() noexcept(false); #else void throwing() throw(int); #endif }; )CPP"; // Default: Off QTest::newRow("none") << cppCode << QByteArray(R"XML( )XML") << false << false << false // exception << false; // allowthread // Modify one function QTest::newRow("modify-function1") << cppCode << QByteArray(R"XML( )XML") << false << false << true // exception << false; // allowthread // Flip defaults by modifying functions QTest::newRow("modify-function2") << cppCode << QByteArray(R"XML( )XML") << true << false << false // exception << false; // allowthread // Activate on type system level QTest::newRow("typesystem-on") << cppCode << QByteArray(R"XML( )XML") << true << false << true // exception << false; // allowthread // Activate on class level QTest::newRow("class-on") << cppCode << QByteArray(R"XML( )XML") << true << false << true // exception << false; // allowthread // Activate on base class level QTest::newRow("baseclass-on") << cppCode << QByteArray(R"XML( )XML") << true << false << true // exception << false; // allowthread // Override value on class level QTest::newRow("override-class-on") << cppCode << QByteArray(R"XML( )XML") << true << false << false // exception << false; // allowthread } void TestModifyFunction::testScopedModifications() { QFETCH(QByteArray, cppCode); QFETCH(QByteArray, xmlCode); QFETCH(bool, expectedGenerateUnspecified); QFETCH(bool, expectedGenerateNonThrowing); QFETCH(bool, expectedGenerateThrowing); QFETCH(bool, expectedAllowThread); QScopedPointer builder(TestUtil::parse(cppCode.constData(), xmlCode.constData(), false)); QVERIFY(builder); const auto classA = AbstractMetaClass::findClass(builder->classes(), "A"); QVERIFY(classA); auto f = classA->findFunction("unspecified"); QVERIFY(f); QCOMPARE(f->exceptionSpecification(), ExceptionSpecification::Unknown); QCOMPARE(f->generateExceptionHandling(), expectedGenerateUnspecified); QCOMPARE(f->allowThread(), expectedAllowThread); f = classA->findFunction("nonThrowing"); QVERIFY(f); QCOMPARE(f->exceptionSpecification(), ExceptionSpecification::NoExcept); QCOMPARE(f->generateExceptionHandling(), expectedGenerateNonThrowing); f = classA->findFunction("throwing"); QVERIFY(f); QCOMPARE(f->exceptionSpecification(), ExceptionSpecification::Throws); QCOMPARE(f->generateExceptionHandling(), expectedGenerateThrowing); } void TestModifyFunction::testSnakeCaseRenaming_data() { QTest::addColumn("name"); QTest::addColumn("expected"); QTest::newRow("s1") << "snakeCaseFunc"_L1 << "snake_case_func"_L1; QTest::newRow("s2") << "SnakeCaseFunc"_L1 << "snake_case_func"_L1; QTest::newRow("consecutive-uppercase") << "snakeCAseFunc"_L1 << "snakeCAseFunc"_L1; } void TestModifyFunction::testSnakeCaseRenaming() { QFETCH(QLatin1StringView, name); QFETCH(QLatin1StringView, expected); const QString actual = AbstractMetaBuilder::getSnakeCaseName(name); QCOMPARE(actual, expected); } QTEST_APPLESS_MAIN(TestModifyFunction)