From e7fdca6465740132bd881ffd9d20e61be47472d0 Mon Sep 17 00:00:00 2001 From: Marcelo Lira Date: Thu, 1 Sep 2011 18:53:22 -0300 Subject: Fixes method's argument types that are templates but the template variable wasn't declared. An unit test was added. Other unrelated tests had minor improvements. Reviewed by Hugo Parente Reviewed by Luciano Wolf --- abstractmetabuilder.cpp | 229 +++++++++++++++++++++++++++++++--------------- tests/testcontainer.cpp | 2 - tests/testenum.cpp | 4 +- tests/testrefcounttag.cpp | 4 +- tests/testtemplates.cpp | 41 +++++++++ tests/testtemplates.h | 1 + 6 files changed, 203 insertions(+), 78 deletions(-) diff --git a/abstractmetabuilder.cpp b/abstractmetabuilder.cpp index 12c0aebde..9fc1674d8 100644 --- a/abstractmetabuilder.cpp +++ b/abstractmetabuilder.cpp @@ -1344,97 +1344,180 @@ void AbstractMetaBuilder::fixReturnTypeOfConversionOperator(AbstractMetaFunction metaFunction->replaceType(metaType); } +static bool _compareAbstractMetaTypes(const AbstractMetaType* type, const AbstractMetaType* other) +{ + if (!type && !other) + return true; + if (!type || !other) + return false; + return type->typeEntry() == other->typeEntry() + && type->isConstant() == other->isConstant() + && type->isReference() == other->isReference() + && type->indirections() == other->indirections(); +} + +static bool _compareAbstractMetaFunctions(const AbstractMetaFunction* func, const AbstractMetaFunction* other) +{ + if (!func && !other) + return true; + if (!func || !other) + return false; + if (func->arguments().count() != other->arguments().count() + || func->isConstant() != other->isConstant() + || func->isStatic() != other->isStatic() + || !_compareAbstractMetaTypes(func->type(), other->type())) { + return false; + } + for (int i = 0; i < func->arguments().count(); ++i) { + if (!_compareAbstractMetaTypes(func->arguments().at(i)->type(), other->arguments().at(i)->type())) + return false; + } + return true; +} + +static bool _fixFunctionModelItemType(TypeInfo& type, const AbstractMetaClass* metaClass) +{ + if (metaClass->templateArguments().isEmpty() + || type.qualifiedName().isEmpty() + || type.qualifiedName().first() != metaClass->typeEntry()->qualifiedCppName()) { + return false; + } + QStringList templateTypes; + foreach (TypeEntry* templateType, metaClass->templateArguments()) + templateTypes << templateType->qualifiedCppName(); + QString fixedTypeName = QString("%1<%2 >").arg(metaClass->typeEntry()->qualifiedCppName()).arg(templateTypes.join(", ")); + type.setQualifiedName(QStringList(fixedTypeName)); + return true; +} + +static bool _fixFunctionModelItemTypes(FunctionModelItem& function, const AbstractMetaClass* metaClass) +{ + TypeInfo functionType = function->type(); + bool templateTypeFixed = _fixFunctionModelItemType(functionType, metaClass); + if (templateTypeFixed) + function->setType(functionType); + + ArgumentList arguments = function->arguments(); + for (int i = 0; i < arguments.size(); ++i) { + ArgumentModelItem arg = arguments.at(i); + TypeInfo type = arg->type(); + bool tmpTypeFixed = _fixFunctionModelItemType(type, metaClass); + if (tmpTypeFixed) + arg->setType(type); + templateTypeFixed |= tmpTypeFixed; + } + return templateTypeFixed; +} + void AbstractMetaBuilder::traverseFunctions(ScopeModelItem scopeItem, AbstractMetaClass* metaClass) { foreach (FunctionModelItem function, scopeItem->functions()) { + + // This fixes method's arguments and return types that are templates + // but the template variable wasn't declared in the C++ header. + bool templateTypeFixed = _fixFunctionModelItemTypes(function, metaClass); + AbstractMetaFunction* metaFunction = traverseFunction(function); - if (metaFunction) { - metaFunction->setOriginalAttributes(metaFunction->attributes()); - if (metaClass->isNamespace()) - *metaFunction += AbstractMetaAttributes::Static; - - QPropertySpec *read = 0; - if (!metaFunction->isSignal() && (read = metaClass->propertySpecForRead(metaFunction->name()))) { - // Property reader must be in the form " name()" - if (metaFunction->type() && (read->type() == metaFunction->type()->typeEntry()) && (metaFunction->arguments().size() == 0)) { - *metaFunction += AbstractMetaAttributes::PropertyReader; - metaFunction->setPropertySpec(read); - } - } else if (QPropertySpec* write = metaClass->propertySpecForWrite(metaFunction->name())) { - // Property setter must be in the form "void name()" - // make sure the function was created with all aguments, some argument can be missing during the pareser because of errors on typesystem - if ((!metaFunction->type()) && (metaFunction->arguments().size() == 1) && (write->type() == metaFunction->arguments().at(0)->type()->typeEntry())) { - *metaFunction += AbstractMetaAttributes::PropertyWriter; - metaFunction->setPropertySpec(write); - } - } else if (QPropertySpec* reset = metaClass->propertySpecForReset(metaFunction->name())) { - // Property resetter must be in the form "void name()" - if ((!metaFunction->type()) && (metaFunction->arguments().size() == 0)) { - *metaFunction += AbstractMetaAttributes::PropertyResetter; - metaFunction->setPropertySpec(reset); + if (!metaFunction) + continue; + + if (templateTypeFixed) { + foreach (AbstractMetaFunction* func, metaClass->queryFunctionsByName(metaFunction->name())) { + if (_compareAbstractMetaFunctions(metaFunction, func)) { + delete metaFunction; + metaFunction = 0; + break; } } + if (!metaFunction) + continue; + } - // Can not use metaFunction->isCopyConstructor() because - // the function wasn't assigned to its owner class yet. - bool isCopyCtor = false; - if (metaFunction->isConstructor() && metaFunction->arguments().size() == 1) { - const AbstractMetaType* argType = metaFunction->arguments().first()->type(); - isCopyCtor = argType->isConstant() - && argType->isReference() - && argType->typeEntry()->name() == metaFunction->name(); + metaFunction->setOriginalAttributes(metaFunction->attributes()); + if (metaClass->isNamespace()) + *metaFunction += AbstractMetaAttributes::Static; + + QPropertySpec *read = 0; + if (!metaFunction->isSignal() && (read = metaClass->propertySpecForRead(metaFunction->name()))) { + // Property reader must be in the form " name()" + if (metaFunction->type() && (read->type() == metaFunction->type()->typeEntry()) && (metaFunction->arguments().size() == 0)) { + *metaFunction += AbstractMetaAttributes::PropertyReader; + metaFunction->setPropertySpec(read); } - - bool isInvalidDestructor = metaFunction->isDestructor() && metaFunction->isPrivate(); - bool isInvalidConstructor = metaFunction->isConstructor() - && ((metaFunction->isPrivate() && !isCopyCtor) || metaFunction->isInvalid()); - - if ((isInvalidDestructor || isInvalidConstructor) - && !metaClass->hasNonPrivateConstructor()) { - *metaClass += AbstractMetaAttributes::Final; - } else if (metaFunction->isConstructor() && !metaFunction->isPrivate()) { - *metaClass -= AbstractMetaAttributes::Final; - metaClass->setHasNonPrivateConstructor(true); + } else if (QPropertySpec* write = metaClass->propertySpecForWrite(metaFunction->name())) { + // Property setter must be in the form "void name()" + // make sure the function was created with all aguments, some argument can be missing during the pareser because of errors on typesystem + if ((!metaFunction->type()) && (metaFunction->arguments().size() == 1) && (write->type() == metaFunction->arguments().at(0)->type()->typeEntry())) { + *metaFunction += AbstractMetaAttributes::PropertyWriter; + metaFunction->setPropertySpec(write); + } + } else if (QPropertySpec* reset = metaClass->propertySpecForReset(metaFunction->name())) { + // Property resetter must be in the form "void name()" + if ((!metaFunction->type()) && (metaFunction->arguments().size() == 0)) { + *metaFunction += AbstractMetaAttributes::PropertyResetter; + metaFunction->setPropertySpec(reset); } + } - // Classes with virtual destructors should always have a shell class - // (since we aren't registering the destructors, we need this extra check) - if (metaFunction->isDestructor() && !metaFunction->isFinal()) - metaClass->setForceShellClass(true); + // Can not use metaFunction->isCopyConstructor() because + // the function wasn't assigned to its owner class yet. + bool isCopyCtor = false; + if (metaFunction->isConstructor() && metaFunction->arguments().size() == 1) { + const AbstractMetaType* argType = metaFunction->arguments().first()->type(); + isCopyCtor = argType->isConstant() + && argType->isReference() + && argType->typeEntry()->name() == metaFunction->name(); + } - if (!metaFunction->isDestructor() - && !metaFunction->isInvalid() - && !(metaFunction->isPrivate() && metaFunction->isConstructor() && !isCopyCtor)) { + bool isInvalidDestructor = metaFunction->isDestructor() && metaFunction->isPrivate(); + bool isInvalidConstructor = metaFunction->isConstructor() + && ((metaFunction->isPrivate() && !isCopyCtor) || metaFunction->isInvalid()); - setupFunctionDefaults(metaFunction, metaClass); + if ((isInvalidDestructor || isInvalidConstructor) + && !metaClass->hasNonPrivateConstructor()) { + *metaClass += AbstractMetaAttributes::Final; + } else if (metaFunction->isConstructor() && !metaFunction->isPrivate()) { + *metaClass -= AbstractMetaAttributes::Final; + metaClass->setHasNonPrivateConstructor(true); + } - if (metaFunction->isSignal() && metaClass->hasSignal(metaFunction)) { - QString warn = QString("signal '%1' in class '%2' is overloaded.") - .arg(metaFunction->name()).arg(metaClass->name()); - ReportHandler::warning(warn); - } + // Classes with virtual destructors should always have a shell class + // (since we aren't registering the destructors, we need this extra check) + if (metaFunction->isDestructor() && !metaFunction->isFinal()) + metaClass->setForceShellClass(true); - if (metaFunction->isSignal() && !metaClass->isQObject()) { - QString warn = QString("signal '%1' in non-QObject class '%2'") - .arg(metaFunction->name()).arg(metaClass->name()); - ReportHandler::warning(warn); - } + if (!metaFunction->isDestructor() + && !metaFunction->isInvalid() + && !(metaFunction->isPrivate() && metaFunction->isConstructor() && !isCopyCtor)) { - if (metaFunction->isConversionOperator()) - fixReturnTypeOfConversionOperator(metaFunction); + setupFunctionDefaults(metaFunction, metaClass); - metaClass->addFunction(metaFunction); - applyFunctionModifications(metaFunction); - } else if (metaFunction->isDestructor()) { - metaClass->setHasPrivateDestructor(metaFunction->isPrivate()); - metaClass->setHasProtectedDestructor(metaFunction->isProtected()); - metaClass->setHasVirtualDestructor(metaFunction->isVirtual()); + if (metaFunction->isSignal() && metaClass->hasSignal(metaFunction)) { + QString warn = QString("signal '%1' in class '%2' is overloaded.") + .arg(metaFunction->name()).arg(metaClass->name()); + ReportHandler::warning(warn); } - if (!metaFunction->ownerClass()) { - delete metaFunction; - metaFunction = 0; + + if (metaFunction->isSignal() && !metaClass->isQObject()) { + QString warn = QString("signal '%1' in non-QObject class '%2'") + .arg(metaFunction->name()).arg(metaClass->name()); + ReportHandler::warning(warn); } + + if (metaFunction->isConversionOperator()) + fixReturnTypeOfConversionOperator(metaFunction); + + metaClass->addFunction(metaFunction); + applyFunctionModifications(metaFunction); + } else if (metaFunction->isDestructor()) { + metaClass->setHasPrivateDestructor(metaFunction->isPrivate()); + metaClass->setHasProtectedDestructor(metaFunction->isProtected()); + metaClass->setHasVirtualDestructor(metaFunction->isVirtual()); + } + if (!metaFunction->ownerClass()) { + delete metaFunction; + metaFunction = 0; } } @@ -1738,7 +1821,6 @@ AbstractMetaFunction* AbstractMetaBuilder::traverseFunction(FunctionModelItem fu return 0; } - Q_ASSERT(functionItem->functionType() == CodeModel::Normal || functionItem->functionType() == CodeModel::Signal || functionItem->functionType() == CodeModel::Slot); @@ -1845,6 +1927,7 @@ AbstractMetaFunction* AbstractMetaBuilder::traverseFunction(FunctionModelItem fu metaFunction->setInvalid(true); return metaFunction; } + AbstractMetaArgument* metaArgument = createMetaArgument(); metaArgument->setType(metaType); diff --git a/tests/testcontainer.cpp b/tests/testcontainer.cpp index 1f5af51fa..563b75c20 100644 --- a/tests/testcontainer.cpp +++ b/tests/testcontainer.cpp @@ -54,8 +54,6 @@ void TestContainer::testContainerType() QCOMPARE(reinterpret_cast(classA->typeEntry()->baseContainerType())->type(), ContainerTypeEntry::ListContainer); } - - QTEST_APPLESS_MAIN(TestContainer) #include "testcontainer.moc" diff --git a/tests/testenum.cpp b/tests/testenum.cpp index 07aa0e8cb..85796a1e9 100644 --- a/tests/testenum.cpp +++ b/tests/testenum.cpp @@ -62,7 +62,9 @@ void TestEnum::testEnumCppSignature() // enum as parameter of a method AbstractMetaClass* classA = classes.findClass("A"); QCOMPARE(classA->enums().count(), 1); - AbstractMetaFunction* method = classA->queryFunctionsByName("method").first(); + AbstractMetaFunctionList funcs = classA->queryFunctionsByName("method"); + QVERIFY(!funcs.isEmpty()); + AbstractMetaFunction* method = funcs.first(); QVERIFY(method); AbstractMetaArgument* arg = method->arguments().first(); QCOMPARE(arg->type()->name(), QString("ClassEnum")); diff --git a/tests/testrefcounttag.cpp b/tests/testrefcounttag.cpp index 424055f2c..9e05cee93 100644 --- a/tests/testrefcounttag.cpp +++ b/tests/testrefcounttag.cpp @@ -48,7 +48,7 @@ void TestRefCountTag::testReferenceCountTag() AbstractMetaClassList classes = t.builder()->classes(); AbstractMetaClass* classB = classes.findClass("B"); const AbstractMetaFunction* func = classB->findFunction("keepObject"); - + QVERIFY(func); ReferenceCount refCount = func->modifications().first().argument_mods.first().referenceCounts.first(); QCOMPARE(refCount.action, ReferenceCount::Add); } @@ -80,7 +80,7 @@ void TestRefCountTag::testWithApiVersion() AbstractMetaClassList classes = t.builder()->classes(); AbstractMetaClass* classB = classes.findClass("B"); const AbstractMetaFunction* func = classB->findFunction("keepObject"); - + QVERIFY(func); ReferenceCount refCount = func->modifications().first().argument_mods.first().referenceCounts.first(); QCOMPARE(refCount.action, ReferenceCount::Add); diff --git a/tests/testtemplates.cpp b/tests/testtemplates.cpp index bfee774d6..c6a65d888 100644 --- a/tests/testtemplates.cpp +++ b/tests/testtemplates.cpp @@ -337,6 +337,47 @@ void TestTemplates::testTypedefOfInstantiationOfTemplateClass() QCOMPARE(inst->cppSignature(), QString("NSpace::TypeOne")); } +void TestTemplates::testContainerTypeIncompleteArgument() +{ + const char* cppCode ="\ + template\ + class Vector {\ + void method(const Vector& vector);\ + Vector otherMethod();\ + };\ + template \ + void Vector::method(const Vector& vector) {}\ + Vector Vector::otherMethod() { return Vector(); }\ + typedef Vector IntVector;\ + "; + const char* xmlCode = "\ + \ + \ + \ + \ + "; + + TestUtil t(cppCode, xmlCode, true); + AbstractMetaClassList classes = t.builder()->classes(); + QCOMPARE(classes.count(), 1); + + AbstractMetaClass* vector = classes.findClass("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("method"); + QVERIFY(method); + QCOMPARE(method->signature(), QString("method(const Vector & vector)")); + + const AbstractMetaFunction* otherMethod = vector->findFunction("otherMethod"); + QVERIFY(otherMethod); + QCOMPARE(otherMethod->signature(), QString("otherMethod()")); + QVERIFY(otherMethod->type()); + QCOMPARE(otherMethod->type()->cppSignature(), QString("Vector")); +} + QTEST_APPLESS_MAIN(TestTemplates) #include "testtemplates.moc" diff --git a/tests/testtemplates.h b/tests/testtemplates.h index 1b7267af4..578691169 100644 --- a/tests/testtemplates.h +++ b/tests/testtemplates.h @@ -39,6 +39,7 @@ private slots: void testTemplateInheritanceMixedWithForwardDeclaration(); void testTemplateInheritanceMixedWithNamespaceAndForwardDeclaration(); void testTypedefOfInstantiationOfTemplateClass(); + void testContainerTypeIncompleteArgument(); }; #endif -- cgit v1.2.3