/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the test suite of Qt for Python. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "testtemplates.h" #include #include #include #include "testutil.h" #include #include void TestTemplates::testTemplateWithNamespace() { const char cppCode[] = R"CPP( template struct QList {}; struct Url { void name(); }; namespace Internet { struct Url{}; struct Bookmarks { QList list(); }; }; )CPP"; const char xmlCode0[] = R"XML( )XML"; QTemporaryFile file; QVERIFY(file.open()); file.write(xmlCode0); file.close(); QString xmlCode1 = QString::fromLatin1(R"XML( )XML").arg(file.fileName()); QScopedPointer builder(TestUtil::parse(cppCode, qPrintable(xmlCode1), false)); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); AbstractMetaClass* classB = AbstractMetaClass::findClass(classes, QLatin1String("Bookmarks")); QVERIFY(classB); const AbstractMetaFunction* func = classB->findFunction(QLatin1String("list")); AbstractMetaType* funcType = func->type(); QVERIFY(funcType); QCOMPARE(funcType->cppSignature(), QLatin1String("QList")); } void TestTemplates::testTemplateOnContainers() { const char cppCode[] = R"CPP( struct Base {}; template struct QList {}; namespace Namespace { enum SomeEnum { E1, E2 }; template struct A { A foo(const QList >& a); }; typedef A B; } )CPP"; const char xmlCode[] = R"XML( )XML"; QScopedPointer builder(TestUtil::parse(cppCode, xmlCode, false)); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); AbstractMetaClass* classB = AbstractMetaClass::findClass(classes, QLatin1String("B")); QVERIFY(classB); QVERIFY(!classB->baseClass()); QVERIFY(classB->baseClassName().isNull()); const AbstractMetaFunction* func = classB->findFunction(QLatin1String("foo")); AbstractMetaType* argType = func->arguments().first()->type(); QCOMPARE(argType->instantiations().count(), 1); QCOMPARE(argType->typeEntry()->qualifiedCppName(), QLatin1String("QList")); const AbstractMetaType* instance1 = argType->instantiations().first(); QCOMPARE(instance1->instantiations().count(), 1); QCOMPARE(instance1->typeEntry()->qualifiedCppName(), QLatin1String("Namespace::A")); const AbstractMetaType* instance2 = instance1->instantiations().first(); QCOMPARE(instance2->instantiations().count(), 0); QCOMPARE(instance2->typeEntry()->qualifiedCppName(), QLatin1String("Namespace::E1")); } void TestTemplates::testTemplateValueAsArgument() { const char cppCode[] = R"CPP( template struct List {}; void func(List arg) {} )CPP"; const char xmlCode[] = R"XML( )XML"; QScopedPointer builder(TestUtil::parse(cppCode, xmlCode, false)); QVERIFY(!builder.isNull()); AbstractMetaFunctionList globalFuncs = builder->globalFunctions(); QCOMPARE(globalFuncs.count(), 1); AbstractMetaFunction* func = globalFuncs.first(); QCOMPARE(func->minimalSignature(), QLatin1String("func(List)")); QCOMPARE(func->arguments().first()->type()->cppSignature(), QLatin1String("List")); } void TestTemplates::testTemplatePointerAsArgument() { const char cppCode[] = R"CPP( template struct List {}; void func(List* arg) {} )CPP"; const char xmlCode[] = R"XML( )XML"; QScopedPointer builder(TestUtil::parse(cppCode, xmlCode, false)); QVERIFY(!builder.isNull()); AbstractMetaFunctionList globalFuncs = builder->globalFunctions(); QCOMPARE(globalFuncs.count(), 1); AbstractMetaFunction* func = globalFuncs.first(); QCOMPARE(func->minimalSignature(), QLatin1String("func(List*)")); QCOMPARE(func->arguments().first()->type()->cppSignature(), QLatin1String("List *")); } void TestTemplates::testTemplateReferenceAsArgument() { const char cppCode[] = R"CPP( template struct List {}; void func(List& arg) {} )CPP"; const char xmlCode[] = R"XML( )XML"; QScopedPointer builder(TestUtil::parse(cppCode, xmlCode, false)); QVERIFY(!builder.isNull()); AbstractMetaFunctionList globalFuncs = builder->globalFunctions(); QCOMPARE(globalFuncs.count(), 1); AbstractMetaFunction* func = globalFuncs.first(); QCOMPARE(func->minimalSignature(), QLatin1String("func(List&)")); QCOMPARE(func->arguments().first()->type()->cppSignature(), QLatin1String("List &")); } void TestTemplates::testTemplateParameterFixup() { const char cppCode[] = R"CPP( template struct List { struct Iterator {}; void append(List l); void erase(List::Iterator it); }; )CPP"; const char xmlCode[] = R"XML( )XML"; QScopedPointer builder(TestUtil::parse(cppCode, xmlCode, false)); QVERIFY(!builder.isNull()); const AbstractMetaClassList templates = builder->templates(); QCOMPARE(templates.count(), 1); const AbstractMetaClass *list = templates.first(); // Verify that the parameter of "void append(List l)" gets fixed to "List" const AbstractMetaFunction *append = list->findFunction(QStringLiteral("append")); QVERIFY(append); QCOMPARE(append->arguments().size(), 1); QCOMPARE(append->arguments().at(0)->type()->cppSignature(), QLatin1String("List")); // Verify that the parameter of "void erase(Iterator)" is not modified const AbstractMetaFunction *erase = list->findFunction(QStringLiteral("erase")); QVERIFY(erase); QCOMPARE(erase->arguments().size(), 1); QEXPECT_FAIL("", "Clang: Some other code changes the parameter type", Abort); QCOMPARE(erase->arguments().at(0)->type()->cppSignature(), QLatin1String("List::Iterator")); } void TestTemplates::testInheritanceFromContainterTemplate() { const char cppCode[] = R"CPP( template struct ListContainer { inline void push_front(const T& t); inline T& front(); }; struct FooBar {}; struct FooBars : public ListContainer {}; )CPP"; const char xmlCode[] = R"XML( )XML"; QScopedPointer builder(TestUtil::parse(cppCode, xmlCode, false)); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); AbstractMetaClassList templates = builder->templates(); QCOMPARE(classes.count(), 2); QCOMPARE(templates.count(), 1); const AbstractMetaClass* foobars = AbstractMetaClass::findClass(classes, QLatin1String("FooBars")); QCOMPARE(foobars->functions().count(), 4); const AbstractMetaClass* lc = templates.first(); QCOMPARE(lc->functions().count(), 2); } void TestTemplates::testTemplateInheritanceMixedWithForwardDeclaration() { const char cppCode[] = R"CPP( enum SomeEnum { E1, E2 }; template struct Future; template struct A { A(); void method(); friend struct Future; }; typedef A B; template struct Future {}; )CPP"; const char xmlCode[] = R"XML( )XML"; QScopedPointer builder(TestUtil::parse(cppCode, xmlCode, false)); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); AbstractMetaClass* classB = AbstractMetaClass::findClass(classes, QLatin1String("B")); QVERIFY(classB); QVERIFY(!classB->baseClass()); QVERIFY(classB->baseClassName().isNull()); // 3 functions: simple constructor, copy constructor and "method()". QCOMPARE(classB->functions().count(), 3); } void TestTemplates::testTemplateInheritanceMixedWithNamespaceAndForwardDeclaration() { const char cppCode[] = R"CPP( namespace Namespace { enum SomeEnum { E1, E2 }; template struct Future; template struct A { A(); void method(); friend struct Future; }; typedef A B; template struct Future {}; }; )CPP"; const char xmlCode[] = R"XML( )XML"; QScopedPointer builder(TestUtil::parse(cppCode, xmlCode, false)); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); AbstractMetaClass* classB = AbstractMetaClass::findClass(classes, QLatin1String("Namespace::B")); QVERIFY(classB); QVERIFY(!classB->baseClass()); QVERIFY(classB->baseClassName().isNull()); // 3 functions: simple constructor, copy constructor and "method()". QCOMPARE(classB->functions().count(), 3); } void TestTemplates::testTypedefOfInstantiationOfTemplateClass() { const char cppCode[] = R"CPP( namespace NSpace { enum ClassType { TypeOne }; template struct BaseTemplateClass { inline ClassType getClassType() const { CLASS_TYPE; } }; typedef BaseTemplateClass TypeOneClass; } )CPP"; const char xmlCode[] = R"XML( )XML"; QScopedPointer builder(TestUtil::parse(cppCode, xmlCode, false)); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); QCOMPARE(classes.count(), 3); const AbstractMetaClass* base = AbstractMetaClass::findClass(classes, QLatin1String("BaseTemplateClass")); QVERIFY(base); const AbstractMetaClass* one = AbstractMetaClass::findClass(classes, QLatin1String("TypeOneClass")); QVERIFY(one); QCOMPARE(one->templateBaseClass(), base); QCOMPARE(one->functions().count(), base->functions().count()); QVERIFY(one->isTypeDef()); const ComplexTypeEntry* oneType = one->typeEntry(); const ComplexTypeEntry* baseType = base->typeEntry(); QCOMPARE(oneType->baseContainerType(), baseType); QCOMPARE(one->baseClassNames(), QStringList(QLatin1String("BaseTemplateClass"))); QVERIFY(one->hasTemplateBaseClassInstantiations()); AbstractMetaTypeList instantiations = one->templateBaseClassInstantiations(); QCOMPARE(instantiations.count(), 1); const AbstractMetaType* inst = instantiations.first(); QVERIFY(inst); QVERIFY(!inst->isEnum()); QVERIFY(!inst->typeEntry()->isEnum()); QVERIFY(inst->typeEntry()->isEnumValue()); QCOMPARE(inst->cppSignature(), QLatin1String("NSpace::TypeOne")); } void TestTemplates::testContainerTypeIncompleteArgument() { const char cppCode[] = R"CPP( template class Vector { void method(const Vector& vector); Vector otherMethod(); }; template void Vector::method(const Vector& vector) {} template Vector Vector::otherMethod() { return Vector(); } typedef Vector IntVector; )CPP"; const char xmlCode[] = R"XML( )XML"; QScopedPointer builder(TestUtil::parse(cppCode, xmlCode, true)); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); QCOMPARE(classes.count(), 1); AbstractMetaClass* vector = AbstractMetaClass::findClass(classes, QLatin1String("IntVector")); QVERIFY(vector); QVERIFY(vector->typeEntry()->baseContainerType()); QCOMPARE(reinterpret_cast(vector->typeEntry()->baseContainerType())->type(), ContainerTypeEntry::VectorContainer); QCOMPARE(vector->functions().count(), 4); const AbstractMetaFunction* method = vector->findFunction(QLatin1String("method")); QVERIFY(method); QCOMPARE(method->signature(), QLatin1String("method(const Vector & vector)")); const AbstractMetaFunction* otherMethod = vector->findFunction(QLatin1String("otherMethod")); QVERIFY(otherMethod); QCOMPARE(otherMethod->signature(), QLatin1String("otherMethod()")); QVERIFY(otherMethod->type()); QCOMPARE(otherMethod->type()->cppSignature(), QLatin1String("Vector")); } // Perform checks on template inheritance; a typedef of a template class // should result in rewritten types. void TestTemplates::testTemplateTypeDefs_data() { QTest::addColumn("cpp"); QTest::addColumn("xml"); const char optionalClassDef[] = R"CPP( template // Some value type similar to std::optional class Optional { public: T value() const { return m_value; } operator bool() const { return m_success; } T m_value; bool m_success = false; }; )CPP"; const char xmlPrefix[] = R"XML( )XML"; const char xmlOptionalDecl[] = "\n"; const char xmlOptionalIntDecl[] = "\n"; const char xmlPostFix[] = "\n"; // Flat, global namespace QString cpp; QTextStream(&cpp) << optionalClassDef << "typedef Optional IntOptional;\n"; QString xml; QTextStream(&xml) << xmlPrefix << xmlOptionalDecl << xmlOptionalIntDecl << "" << xmlPostFix; QTest::newRow("global-namespace") << cpp << xml; // Typedef from namespace Std cpp.clear(); QTextStream(&cpp) << "namespace Std {\n" << optionalClassDef << "}\n" << "typedef Std::Optional IntOptional;\n"; xml.clear(); QTextStream(&xml) << xmlPrefix << "\n" << xmlOptionalDecl << "\n" << xmlOptionalIntDecl << "" << xmlPostFix; QTest::newRow("namespace-Std") << cpp << xml; // Typedef from nested class cpp.clear(); QTextStream(&cpp) << "class Outer {\npublic:\n" << optionalClassDef << "\n};\n" << "typedef Outer::Optional IntOptional;\n"; xml.clear(); QTextStream(&xml) << xmlPrefix << "\n" << xmlOptionalDecl << "\n" << xmlOptionalIntDecl << "" << xmlPostFix; QTest::newRow("nested-class") << cpp << xml; } void TestTemplates::testTemplateTypeDefs() { QFETCH(QString, cpp); QFETCH(QString, xml); const QByteArray cppBa = cpp.toLocal8Bit(); const QByteArray xmlBa = xml.toLocal8Bit(); QScopedPointer builder(TestUtil::parse(cppBa.constData(), xmlBa.constData(), true)); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); const AbstractMetaClass *optional = AbstractMetaClass::findClass(classes, QLatin1String("Optional")); QVERIFY(optional); // Find the typedef'ed class const AbstractMetaClass *optionalInt = AbstractMetaClass::findClass(classes, QLatin1String("IntOptional")); QVERIFY(optionalInt); QCOMPARE(optionalInt->templateBaseClass(), optional); // Find the class typedef'ed in the typesystem XML const AbstractMetaClass *xmlOptionalInt = AbstractMetaClass::findClass(classes, QLatin1String("XmlIntOptional")); QVERIFY(xmlOptionalInt); QCOMPARE(xmlOptionalInt->templateBaseClass(), optional); // Check whether the value() method now has an 'int' return const AbstractMetaFunction *valueMethod = optionalInt->findFunction(QLatin1String("value")); QVERIFY(valueMethod); QCOMPARE(valueMethod->type()->cppSignature(), QLatin1String("int")); // ditto for typesystem XML const AbstractMetaFunction *xmlValueMethod = xmlOptionalInt->findFunction(QLatin1String("value")); QVERIFY(xmlValueMethod); QCOMPARE(xmlValueMethod->type()->cppSignature(), QLatin1String("int")); // Check whether the m_value field is of type 'int' const AbstractMetaField *valueField = optionalInt->findField(QLatin1String("m_value")); QVERIFY(valueField); QCOMPARE(valueField->type()->cppSignature(), QLatin1String("int")); // ditto for typesystem XML const AbstractMetaField *xmlValueField = xmlOptionalInt->findField(QLatin1String("m_value")); QVERIFY(xmlValueField); QCOMPARE(xmlValueField->type()->cppSignature(), QLatin1String("int")); } QTEST_APPLESS_MAIN(TestTemplates)