/**************************************************************************** ** ** 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 #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 auto func = classB->findFunction(QLatin1String("list")); QVERIFY(!func.isNull()); AbstractMetaType funcType = func->type(); QVERIFY(!funcType.isVoid()); 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().isEmpty()); const auto func = classB->findFunction(QLatin1String("foo")); QVERIFY(!func.isNull()); AbstractMetaType argType = func->arguments().constFirst().type(); QCOMPARE(argType.instantiations().size(), 1); QCOMPARE(argType.typeEntry()->qualifiedCppName(), QLatin1String("QList")); const AbstractMetaType &instance1 = argType.instantiations().constFirst(); QCOMPARE(instance1.instantiations().size(), 1); QCOMPARE(instance1.typeEntry()->qualifiedCppName(), QLatin1String("Namespace::A")); const AbstractMetaType &instance2 = instance1.instantiations().constFirst(); QCOMPARE(instance2.instantiations().size(), 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()); const auto globalFuncs = builder->globalFunctions(); QCOMPARE(globalFuncs.size(), 1); const auto func = globalFuncs.constFirst(); QCOMPARE(func->minimalSignature(), QLatin1String("func(List)")); QCOMPARE(func->arguments().constFirst().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()); AbstractMetaFunctionCList globalFuncs = builder->globalFunctions(); QCOMPARE(globalFuncs.size(), 1); const auto func = globalFuncs.constFirst(); QCOMPARE(func->minimalSignature(), QLatin1String("func(List*)")); QCOMPARE(func->arguments().constFirst().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()); const auto globalFuncs = builder->globalFunctions(); QCOMPARE(globalFuncs.size(), 1); const auto func = globalFuncs.constFirst(); QCOMPARE(func->minimalSignature(), QLatin1String("func(List&)")); QCOMPARE(func->arguments().constFirst().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.size(), 1); const AbstractMetaClass *list = templates.constFirst(); // Verify that the parameter of "void append(List l)" gets fixed to "List" const auto append = list->findFunction(QStringLiteral("append")); QVERIFY(!append.isNull()); 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 auto erase = list->findFunction(QStringLiteral("erase")); QVERIFY(!erase.isNull()); 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.size(), 2); QCOMPARE(templates.size(), 1); const AbstractMetaClass* foobars = AbstractMetaClass::findClass(classes, QLatin1String("FooBars")); QCOMPARE(foobars->functions().size(), 4); const AbstractMetaClass *lc = templates.constFirst(); QCOMPARE(lc->functions().size(), 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().isEmpty()); // 3 functions: simple constructor, copy constructor and "method()". QCOMPARE(classB->functions().size(), 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().isEmpty()); // 3 functions: simple constructor, copy constructor and "method()". QCOMPARE(classB->functions().size(), 3); } void TestTemplates::testTypedefOfInstantiationOfTemplateClass() { const char cppCode[] = R"CPP( namespace NSpace { enum ClassType { TypeOne }; template struct BaseTemplateClass { inline ClassType getClassType() const { return 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.size(), 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().size(), base->functions().size()); 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.size(), 1); const AbstractMetaType &inst = instantiations.constFirst(); 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.size(), 1); AbstractMetaClass* vector = AbstractMetaClass::findClass(classes, QLatin1String("IntVector")); QVERIFY(vector); auto baseContainer = vector->typeEntry()->baseContainerType(); QVERIFY(baseContainer); QCOMPARE(reinterpret_cast(baseContainer)->containerKind(), ContainerTypeEntry::ListContainer); QCOMPARE(vector->functions().size(), 4); const auto method = vector->findFunction(QLatin1String("method")); QVERIFY(!method.isNull()); QCOMPARE(method->signature(), QLatin1String("method(const Vector & vector)")); const auto otherMethod = vector->findFunction(QLatin1String("otherMethod")); QVERIFY(!otherMethod.isNull()); QCOMPARE(otherMethod->signature(), QLatin1String("otherMethod()")); QVERIFY(!otherMethod->type().isVoid()); QCOMPARE(otherMethod->type().cppSignature(), QLatin1String("Vector")); } void TestTemplates::testNonTypeTemplates() { // PYSIDe-1296, functions with non type templates parameters. const char cppCode[] = R"CPP( template class Array { T array[Size]; }; Array foo(); )CPP"; const char xmlCode[] = R"XML( )XML"; QScopedPointer builder(TestUtil::parse(cppCode, xmlCode, true)); QVERIFY(!builder.isNull()); auto functions = builder->globalFunctions(); QCOMPARE(functions.size(), 1); auto foo = functions.constFirst(); QCOMPARE(foo->name(), QLatin1String("foo")); QCOMPARE(foo->type().name(), QLatin1String("Array")); } // 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 auto valueMethod = optionalInt->findFunction(QLatin1String("value")); QVERIFY(!valueMethod.isNull()); QCOMPARE(valueMethod->type().cppSignature(), QLatin1String("int")); // ditto for typesystem XML const auto xmlValueMethod = xmlOptionalInt->findFunction(QLatin1String("value")); QVERIFY(!xmlValueMethod.isNull()); QCOMPARE(xmlValueMethod->type().cppSignature(), QLatin1String("int")); // Check whether the m_value field is of type 'int' const auto valueField = optionalInt->findField(u"m_value"); QVERIFY(valueField.has_value()); QCOMPARE(valueField->type().cppSignature(), QLatin1String("int")); // ditto for typesystem XML const auto xmlValueField = xmlOptionalInt->findField(u"m_value"); QVERIFY(xmlValueField.has_value()); QCOMPARE(xmlValueField->type().cppSignature(), QLatin1String("int")); } void TestTemplates::testTemplateTypeAliases() { // Model Qt 6's "template using QList = QVector" const char cppCode[] = R"CPP( template class Container1 { }; template using Container2 = Container1; class Test { public: Container2 m_intContainer; }; class Derived : public Container2 { public: }; )CPP"; const char xmlCode[] = R"XML( )XML"; QScopedPointer builder(TestUtil::parse(cppCode, xmlCode, true)); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); auto testClass = AbstractMetaClass::findClass(classes, QLatin1String("Test")); QVERIFY(testClass); auto fields = testClass->fields(); QCOMPARE(fields.size(), 1); auto fieldType = testClass->fields().at(0).type(); QCOMPARE(fieldType.name(), QLatin1String("Container1")); QCOMPARE(fieldType.instantiations().size(), 1); auto derived = AbstractMetaClass::findClass(classes, QLatin1String("Derived")); QVERIFY(derived); auto base = derived->templateBaseClass(); QCOMPARE(base->name(), QLatin1String("Container1")); } QTEST_APPLESS_MAIN(TestTemplates)