diff options
Diffstat (limited to 'sources/shiboken6/ApiExtractor/tests/testtemplates.cpp')
-rw-r--r-- | sources/shiboken6/ApiExtractor/tests/testtemplates.cpp | 628 |
1 files changed, 628 insertions, 0 deletions
diff --git a/sources/shiboken6/ApiExtractor/tests/testtemplates.cpp b/sources/shiboken6/ApiExtractor/tests/testtemplates.cpp new file mode 100644 index 000000000..ea37c6255 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testtemplates.cpp @@ -0,0 +1,628 @@ +// 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 "testtemplates.h" +#include "testutil.h" +#include <abstractmetaargument.h> +#include <abstractmetafield.h> +#include <abstractmetafunction.h> +#include <abstractmetalang.h> +#include <abstractmetatype.h> +#include <complextypeentry.h> +#include <containertypeentry.h> + +#include <qtcompat.h> + +#include <QtCore/QTemporaryFile> +#include <QtCore/QTextStream> +#include <QtTest/QTest> + +using namespace Qt::StringLiterals; + +void TestTemplates::testTemplateWithNamespace() +{ + const char cppCode[] = R"CPP( +template<typename T> struct QList {}; +struct Url { + void name(); +}; +namespace Internet { + struct Url{}; + struct Bookmarks { + QList<Url> list(); + }; +}; +)CPP"; + + const char xmlCode0[] = R"XML( +<typesystem package='Package.Network'> + <value-type name='Url'/> +</typesystem>)XML"; + + QTemporaryFile file; + QVERIFY(file.open()); + file.write(xmlCode0); + file.close(); + + QString xmlCode1 = QString::fromLatin1(R"XML( +<typesystem package='Package.Internet'> + <load-typesystem name='%1' generate='no'/> + <container-type name='QList' type='list'/> + <namespace-type name='Internet' generate='no'> + <value-type name='Url'/> + <value-type name='Bookmarks'/> + </namespace-type> +</typesystem>)XML").arg(file.fileName()); + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, qPrintable(xmlCode1), false)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + + const auto classB = AbstractMetaClass::findClass(classes, "Bookmarks"); + QVERIFY(classB); + const auto func = classB->findFunction("list"); + QVERIFY(func); + AbstractMetaType funcType = func->type(); + QVERIFY(!funcType.isVoid()); + QCOMPARE(funcType.cppSignature(), u"QList<Internet::Url>"); +} + +void TestTemplates::testTemplateOnContainers() +{ + const char cppCode[] = R"CPP( +struct Base {}; +template<typename T> struct QList {}; +namespace Namespace { + enum SomeEnum { E1, E2 }; + template<SomeEnum type> struct A { + A<type> foo(const QList<A<type> >& a); + }; + typedef A<E1> B; +} +)CPP"; + + const char xmlCode[] = R"XML( +<typesystem package="Package"> + <container-type name='QList' type='list'/> + <namespace-type name='Namespace'> + <enum-type name='SomeEnum'/> + <object-type name='A' generate='no'/> + <object-type name='B'/> + </namespace-type> + <object-type name='Base'/> +</typesystem>)XML"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + + const auto classB = AbstractMetaClass::findClass(classes, "B"); + QVERIFY(classB); + QVERIFY(!classB->baseClass()); + QVERIFY(classB->baseClassName().isEmpty()); + const auto func = classB->findFunction("foo"); + QVERIFY(func); + AbstractMetaType argType = func->arguments().constFirst().type(); + QCOMPARE(argType.instantiations().size(), 1); + QCOMPARE(argType.typeEntry()->qualifiedCppName(), u"QList"); + + const AbstractMetaType &instance1 = argType.instantiations().constFirst(); + QCOMPARE(instance1.instantiations().size(), 1); + QCOMPARE(instance1.typeEntry()->qualifiedCppName(), u"Namespace::A"); + + const AbstractMetaType &instance2 = instance1.instantiations().constFirst(); + QCOMPARE(instance2.instantiations().size(), 0); + QCOMPARE(instance2.typeEntry()->qualifiedCppName(), u"Namespace::E1"); +} + +void TestTemplates::testTemplateValueAsArgument() +{ + const char cppCode[] = R"CPP( +template<typename T> struct List {}; +void func(List<int> arg) {} +)CPP"; + + const char xmlCode[] = R"XML( +<typesystem package='Package'> + <primitive-type name='int'/> + <container-type name='List' type='list'/> + <function signature='func(List<int>)'/> +</typesystem>)XML"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + const auto globalFuncs = builder->globalFunctions(); + QCOMPARE(globalFuncs.size(), 1); + + const auto func = globalFuncs.constFirst(); + QCOMPARE(func->minimalSignature(), u"func(List<int>)"); + QCOMPARE(func->arguments().constFirst().type().cppSignature(), + u"List<int>"); +} + +void TestTemplates::testTemplatePointerAsArgument() +{ + const char cppCode[] = R"CPP( +template<typename T> struct List {}; +void func(List<int>* arg) {} +)CPP"; + + const char xmlCode[] = R"XML( + <typesystem package='Package'> + <primitive-type name='int'/> + <container-type name='List' type='list'/> + <function signature='func(List<int>*)'/> + </typesystem>)XML"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + AbstractMetaFunctionCList globalFuncs = builder->globalFunctions(); + QCOMPARE(globalFuncs.size(), 1); + + const auto func = globalFuncs.constFirst(); + QCOMPARE(func->minimalSignature(), u"func(List<int>*)"); + QCOMPARE(func->arguments().constFirst().type().cppSignature(), + u"List<int> *"); +} + +void TestTemplates::testTemplateReferenceAsArgument() +{ + const char cppCode[] = R"CPP( +template<typename T> struct List {}; +void func(List<int>& arg) {} + )CPP"; + + const char xmlCode[] = R"XML( +<typesystem package='Package'> + <primitive-type name='int'/> + <container-type name='List' type='list'/> + <function signature='func(List<int>&)'/> +</typesystem>)XML"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + const auto globalFuncs = builder->globalFunctions(); + QCOMPARE(globalFuncs.size(), 1); + + const auto func = globalFuncs.constFirst(); + QCOMPARE(func->minimalSignature(), u"func(List<int>&)"); + QCOMPARE(func->arguments().constFirst().type().cppSignature(), + u"List<int> &"); +} + +void TestTemplates::testTemplateParameterFixup() +{ + const char cppCode[] = R"CPP( +template<typename T> +struct List { + struct Iterator {}; + void append(List l); + void erase(List::Iterator it); +}; +)CPP"; + + const char xmlCode[] = R"XML( + <typesystem package='Package'> + <container-type name='List' type='list'> + <value-type name='Iterator'/> + </container-type> + </typesystem>)XML"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + const AbstractMetaClassList templates = builder->templates(); + + QCOMPARE(templates.size(), 1); + AbstractMetaClassCPtr list = templates.constFirst(); + // Verify that the parameter of "void append(List l)" gets fixed to "List<T>" + const auto append = list->findFunction("append"); + QVERIFY(append); + QCOMPARE(append->arguments().size(), 1); + QCOMPARE(append->arguments().at(0).type().cppSignature(), u"List<T>"); + // Verify that the parameter of "void erase(Iterator)" is not modified + const auto erase = list->findFunction("erase"); + QVERIFY(erase); + QCOMPARE(erase->arguments().size(), 1); + QCOMPARE(erase->arguments().at(0).type().cppSignature(), u"List::Iterator"); +} + +void TestTemplates::testInheritanceFromContainterTemplate() +{ + const char cppCode[] = R"CPP( +template<typename T> +struct ListContainer { + inline void push_front(const T& t); + inline T& front(); +}; +struct FooBar {}; +struct FooBars : public ListContainer<FooBar> {}; +)CPP"; + + const char xmlCode[] = R"XML( +<typesystem package='Package'> + <container-type name='ListContainer' type='list'/> + <value-type name='FooBar'/> + <value-type name='FooBars'> + <modify-function signature='push_front(FooBar)' remove='all'/> + <modify-function signature='front()' remove='all'/> + </value-type> +</typesystem>)XML"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + AbstractMetaClassList templates = builder->templates(); + QCOMPARE(classes.size(), 2); + QCOMPARE(templates.size(), 1); + + const auto foobars = AbstractMetaClass::findClass(classes, "FooBars"); + QCOMPARE(foobars->functions().size(), 4); + + AbstractMetaClassCPtr lc = templates.constFirst(); + QCOMPARE(lc->functions().size(), 2); +} + +void TestTemplates::testTemplateInheritanceMixedWithForwardDeclaration() +{ + const char cppCode[] = R"CPP( +enum SomeEnum { E1, E2 }; +template<SomeEnum type> struct Future; +template<SomeEnum type> +struct A { + A(); + void method(); + friend struct Future<type>; +}; +typedef A<E1> B; +template<SomeEnum type> struct Future {}; +)CPP"; + + const char xmlCode[] = R"XML( +<typesystem package='Package'> + <enum-type name='SomeEnum'/> + <value-type name='A' generate='no'/> + <value-type name='B'/> + <value-type name='Future' generate='no'/> +</typesystem>)XML"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + + const auto classB = AbstractMetaClass::findClass(classes, "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<SomeEnum type> struct Future; +template<SomeEnum type> +struct A { + A(); + void method(); + friend struct Future<type>; +}; +typedef A<E1> B; +template<SomeEnum type> struct Future {}; +}; +)CPP"; + + const char xmlCode[] = R"XML( +<typesystem package='Package'> + <namespace-type name='Namespace'> + <enum-type name='SomeEnum'/> + <value-type name='A' generate='no'/> + <value-type name='B'/> + <value-type name='Future' generate='no'/> + </namespace-type> +</typesystem>)XML"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + + const auto classB = AbstractMetaClass::findClass(classes, "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<ClassType CLASS_TYPE> +struct BaseTemplateClass { + inline ClassType getClassType() const { return CLASS_TYPE; } +}; +typedef BaseTemplateClass<TypeOne> TypeOneClass; +} +)CPP"; + + const char xmlCode[] = R"XML( +<typesystem package='Package'> + <namespace-type name='NSpace'> + <enum-type name='ClassType'/> + <object-type name='BaseTemplateClass' generate='no'/> + <object-type name='TypeOneClass'/> + </namespace-type> +</typesystem>)XML"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 3); + + const auto base = AbstractMetaClass::findClass(classes, "BaseTemplateClass"); + QVERIFY(base); + const auto one = AbstractMetaClass::findClass(classes, "TypeOneClass"); + QVERIFY(one); + QCOMPARE(one->templateBaseClass(), base); + QCOMPARE(one->functions().size(), base->functions().size()); + QVERIFY(one->isTypeDef()); + auto oneType = one->typeEntry(); + auto baseType = base->typeEntry(); + QCOMPARE(oneType->baseContainerType(), baseType); + QCOMPARE(one->baseClassNames(), QStringList(u"NSpace::BaseTemplateClass<NSpace::TypeOne>"_s)); + + 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(), u"NSpace::TypeOne"); +} + +void TestTemplates::testContainerTypeIncompleteArgument() +{ + const char cppCode[] = R"CPP( +template<typename T> +class Vector { + void method(const Vector& vector); + Vector otherMethod(); +}; +template <typename T> +void Vector<T>::method(const Vector<T>& vector) {} +template <typename T> +Vector<T> Vector<T>::otherMethod() { return Vector<T>(); } +typedef Vector<int> IntVector; +)CPP"; + + const char xmlCode[] = R"XML( +<typesystem package='Foo'> + <primitive-type name='int'/> + <container-type name='Vector' type='vector'/> + <value-type name='IntVector'/> +</typesystem>)XML"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, true)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 1); + + const auto vector = AbstractMetaClass::findClass(classes, "IntVector"); + QVERIFY(vector); + auto baseContainer = vector->typeEntry()->baseContainerType(); + QVERIFY(baseContainer); + QCOMPARE(reinterpret_cast<const ContainerTypeEntry*>(baseContainer.get())->containerKind(), + ContainerTypeEntry::ListContainer); + QCOMPARE(vector->functions().size(), 4); + + const auto method = vector->findFunction("method"); + QVERIFY(method); + QCOMPARE(method->signature(), u"method(const Vector<int> & vector)"); + + const auto otherMethod = vector->findFunction("otherMethod"); + QVERIFY(otherMethod); + QCOMPARE(otherMethod->signature(), u"otherMethod()"); + QVERIFY(!otherMethod->type().isVoid()); + QCOMPARE(otherMethod->type().cppSignature(), u"Vector<int>"); +} + +void TestTemplates::testNonTypeTemplates() +{ + // PYSIDe-1296, functions with non type templates parameters. + const char cppCode[] = R"CPP( +template <class T, int Size> +class Array { + T array[Size]; +}; + +Array<int, 2> foo(); + +)CPP"; + + const char xmlCode[] = R"XML( +<typesystem package='Foo'> + <primitive-type name='int'/> + <container-type name='Array' type='vector'/> + <function signature="foo()"/> +</typesystem>)XML"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, true)); + QVERIFY(builder); + auto functions = builder->globalFunctions(); + QCOMPARE(functions.size(), 1); + auto foo = functions.constFirst(); + QCOMPARE(foo->name(), u"foo"); + QCOMPARE(foo->type().name(), u"Array"); +} + +// Perform checks on template inheritance; a typedef of a template class +// should result in rewritten types. +void TestTemplates::testTemplateTypeDefs_data() +{ + QTest::addColumn<QString>("cpp"); + QTest::addColumn<QString>("xml"); + + const char optionalClassDef[] = R"CPP( +template<class T> // 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( +<typesystem package='Foo'> + <primitive-type name='int'/> + <primitive-type name='bool'/> +)XML"; + + const char xmlOptionalDecl[] = "<value-type name='Optional' generate='no'/>\n"; + const char xmlOptionalIntDecl[] = "<value-type name='IntOptional'/>\n"; + const char xmlPostFix[] = "</typesystem>\n"; + + // Flat, global namespace + QString cpp; + QTextStream(&cpp) << optionalClassDef + << "typedef Optional<int> IntOptional;\n"; + QString xml; + QTextStream(&xml) << xmlPrefix << xmlOptionalDecl << xmlOptionalIntDecl + << "<typedef-type name='XmlIntOptional' source='Optional<int>'/>" + << xmlPostFix; + QTest::newRow("global-namespace") + << cpp << xml; + + // Typedef from namespace Std + cpp.clear(); + QTextStream(&cpp) << "namespace Std {\n" << optionalClassDef << "}\n" + << "typedef Std::Optional<int> IntOptional;\n"; + xml.clear(); + QTextStream(&xml) << xmlPrefix + << "<namespace-type name='Std'>\n" << xmlOptionalDecl + << "</namespace-type>\n" << xmlOptionalIntDecl + << "<typedef-type name='XmlIntOptional' source='Std::Optional<int>'/>" + << 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<int> IntOptional;\n"; + xml.clear(); + QTextStream(&xml) << xmlPrefix + << "<object-type name='Outer'>\n" << xmlOptionalDecl + << "</object-type>\n" << xmlOptionalIntDecl + << "<typedef-type name='XmlIntOptional' source='Outer::Optional<int>'/>" + << 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<AbstractMetaBuilder> builder(TestUtil::parse(cppBa.constData(), xmlBa.constData(), true)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + + const auto optional = AbstractMetaClass::findClass(classes, "Optional"); + QVERIFY(optional); + + // Find the typedef'ed class + const auto optionalInt = AbstractMetaClass::findClass(classes, "IntOptional"); + QVERIFY(optionalInt); + QCOMPARE(optionalInt->templateBaseClass(), optional); + + // Find the class typedef'ed in the typesystem XML + const auto xmlOptionalInt = AbstractMetaClass::findClass(classes, "XmlIntOptional"); + QVERIFY(xmlOptionalInt); + QCOMPARE(xmlOptionalInt->templateBaseClass(), optional); + + // Check whether the value() method now has an 'int' return + const auto valueMethod = optionalInt->findFunction("value"); + QVERIFY(valueMethod); + QCOMPARE(valueMethod->type().cppSignature(), u"int"); + + // ditto for typesystem XML + const auto xmlValueMethod = xmlOptionalInt->findFunction("value"); + QVERIFY(xmlValueMethod); + QCOMPARE(xmlValueMethod->type().cppSignature(), u"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(), u"int"); + + // ditto for typesystem XML + const auto xmlValueField = + xmlOptionalInt->findField(u"m_value"); + QVERIFY(xmlValueField.has_value()); + QCOMPARE(xmlValueField->type().cppSignature(), u"int"); +} + +void TestTemplates::testTemplateTypeAliases() +{ + // Model Qt 6's "template<typename T> using QList = QVector<T>" + const char cppCode[] = R"CPP( +template<typename T> +class Container1 { }; + +template<typename T> +using Container2 = Container1<T>; + +class Test +{ +public: + Container2<int> m_intContainer; +}; + +class Derived : public Container2<int> +{ +public: +}; +)CPP"; + + const char xmlCode[] = R"XML( +<typesystem package='Foo'> + <primitive-type name='int'/> + <value-type name='Container1'/> + <value-type name='Derived'/> + <object-type name='Test'/> +</typesystem>)XML"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, true)); + QVERIFY(builder); + + AbstractMetaClassList classes = builder->classes(); + const auto testClass = AbstractMetaClass::findClass(classes, "Test"); + QVERIFY(testClass); + + auto fields = testClass->fields(); + QCOMPARE(fields.size(), 1); + auto fieldType = testClass->fields().at(0).type(); + QCOMPARE(fieldType.name(), u"Container1"); + QCOMPARE(fieldType.instantiations().size(), 1); + + const auto derived = AbstractMetaClass::findClass(classes, "Derived"); + QVERIFY(derived); + auto base = derived->templateBaseClass(); + QVERIFY(base); + QCOMPARE(base->name(), u"Container1"); +} + +QTEST_APPLESS_MAIN(TestTemplates) |