diff options
author | Nikolai Kosjar <nikolai.kosjar@digia.com> | 2013-12-10 11:38:16 +0100 |
---|---|---|
committer | Nikolai Kosjar <nikolai.kosjar@digia.com> | 2013-12-11 11:46:33 +0100 |
commit | 273192759b08eb356db5492a686b8da6a0d8e3ab (patch) | |
tree | 8f6771055bc661375cbbae6aa335f69b6b5309f5 /src/plugins/cpptools/functionutils.cpp | |
parent | a18b5e27a1605d7fe6183ad388d4a827295d6a3f (diff) | |
download | qt-creator-273192759b08eb356db5492a686b8da6a0d8e3ab.tar.gz |
CppEditor/CppTools: Move FunctionHelper to CppTools
...and rename it to FunctionUtils.
Change-Id: If076ec01fd82e8ba728764bdeab7e87e8bc1ff3b
Reviewed-by: Erik Verbruggen <erik.verbruggen@digia.com>
Diffstat (limited to 'src/plugins/cpptools/functionutils.cpp')
-rw-r--r-- | src/plugins/cpptools/functionutils.cpp | 321 |
1 files changed, 321 insertions, 0 deletions
diff --git a/src/plugins/cpptools/functionutils.cpp b/src/plugins/cpptools/functionutils.cpp new file mode 100644 index 0000000000..fd7ae1d89c --- /dev/null +++ b/src/plugins/cpptools/functionutils.cpp @@ -0,0 +1,321 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "functionutils.h" + +#include "typehierarchybuilder.h" + +#include <cplusplus/CppDocument.h> +#include <cplusplus/LookupContext.h> +#include <cplusplus/Symbols.h> +#include <utils/qtcassert.h> + +#include <QList> + +using namespace CPlusPlus; +using namespace CppTools; + +enum VirtualType { Virtual, PureVirtual }; + +static bool isVirtualFunction_helper(const Function *function, + const LookupContext &context, + VirtualType virtualType, + const Function **firstVirtual) +{ + enum { Unknown, False, True } res = Unknown; + + if (firstVirtual) + *firstVirtual = 0; + + if (!function) + return false; + + if (virtualType == PureVirtual) + res = function->isPureVirtual() ? True : False; + + if (function->isVirtual()) { + if (firstVirtual) + *firstVirtual = function; + if (res == Unknown) + res = True; + } + + if (!firstVirtual && res != Unknown) + return res == True; + + QList<LookupItem> results = context.lookup(function->name(), function->enclosingScope()); + if (!results.isEmpty()) { + const bool isDestructor = function->name()->isDestructorNameId(); + foreach (const LookupItem &item, results) { + if (Symbol *symbol = item.declaration()) { + if (Function *functionType = symbol->type()->asFunctionType()) { + if (functionType->name()->isDestructorNameId() != isDestructor) + continue; + if (functionType == function) // already tested + continue; + if (functionType->isFinal()) + return res == True; + if (functionType->isVirtual()) { + if (!firstVirtual) + return true; + if (res == Unknown) + res = True; + *firstVirtual = functionType; + } + } + } + } + } + + return res == True; +} + +bool FunctionUtils::isVirtualFunction(const Function *function, + const LookupContext &context, + const Function **firstVirtual) +{ + return isVirtualFunction_helper(function, context, Virtual, firstVirtual); +} + +bool FunctionUtils::isPureVirtualFunction(const Function *function, + const LookupContext &context, + const Function **firstVirtual) +{ + return isVirtualFunction_helper(function, context, PureVirtual, firstVirtual); +} + +QList<Symbol *> FunctionUtils::overrides(Function *function, Class *functionsClass, + Class *staticClass, const Snapshot &snapshot) +{ + QList<Symbol *> result; + QTC_ASSERT(function && functionsClass && staticClass, return result); + + FullySpecifiedType referenceType = function->type(); + const Name *referenceName = function->name(); + QTC_ASSERT(referenceName && referenceType.isValid(), return result); + + // Find overrides + TypeHierarchyBuilder builder(staticClass, snapshot); + const TypeHierarchy &staticClassHierarchy = builder.buildDerivedTypeHierarchy(); + + QList<TypeHierarchy> l; + l.append(TypeHierarchy(functionsClass)); + l.append(staticClassHierarchy); + + while (!l.isEmpty()) { + // Add derived + const TypeHierarchy hierarchy = l.takeFirst(); + QTC_ASSERT(hierarchy.symbol(), continue); + Class *c = hierarchy.symbol()->asClass(); + QTC_ASSERT(c, continue); + + foreach (const TypeHierarchy &t, hierarchy.hierarchy()) { + if (!l.contains(t)) + l << t; + } + + // Check member functions + for (int i = 0, total = c->memberCount(); i < total; ++i) { + Symbol *candidate = c->memberAt(i); + const Name *candidateName = candidate->name(); + const FullySpecifiedType candidateType = candidate->type(); + if (!candidateName || !candidateType.isValid()) + continue; + if (candidateName->isEqualTo(referenceName) && candidateType.isEqualTo(referenceType)) + result << candidate; + } + } + + return result; +} + +#ifdef WITH_TESTS +#include "cpptoolsplugin.h" + +#include <QTest> + +namespace CppTools { +namespace Internal { + +enum Virtuality +{ + NotVirtual, + Virtual, + PureVirtual +}; +typedef QList<Virtuality> VirtualityList; +} // Internal namespace +} // CppTools namespace + +Q_DECLARE_METATYPE(CppTools::Internal::Virtuality) +Q_DECLARE_METATYPE(CppTools::Internal::VirtualityList) +Q_DECLARE_METATYPE(QList<int>) + +namespace CppTools { +namespace Internal { + +void CppToolsPlugin::test_functionutils_virtualFunctions() +{ + // Create and parse document + QFETCH(QByteArray, source); + QFETCH(VirtualityList, virtualityList); + QFETCH(QList<int>, firstVirtualList); + Document::Ptr document = Document::create(QLatin1String("virtuals")); + document->setUtf8Source(source); + document->check(); // calls parse(); + QCOMPARE(document->diagnosticMessages().size(), 0); + QVERIFY(document->translationUnit()->ast()); + QList<const Function *> allFunctions; + const Function *firstVirtual = 0; + + // Iterate through Function symbols + Snapshot snapshot; + snapshot.insert(document); + const LookupContext context(document, snapshot); + Control *control = document->translationUnit()->control(); + Symbol **end = control->lastSymbol(); + for (Symbol **it = control->firstSymbol(); it != end; ++it) { + if (const Function *function = (*it)->asFunction()) { + allFunctions.append(function); + QTC_ASSERT(!virtualityList.isEmpty(), return); + Virtuality virtuality = virtualityList.takeFirst(); + QTC_ASSERT(!firstVirtualList.isEmpty(), return); + int firstVirtualIndex = firstVirtualList.takeFirst(); + bool isVirtual = FunctionUtils::isVirtualFunction(function, context, &firstVirtual); + bool isPureVirtual = FunctionUtils::isPureVirtualFunction(function, context, + &firstVirtual); + + // Test for regressions introduced by firstVirtual + QCOMPARE(FunctionUtils::isVirtualFunction(function, context), isVirtual); + QCOMPARE(FunctionUtils::isPureVirtualFunction(function, context), isPureVirtual); + if (isVirtual) { + if (isPureVirtual) + QCOMPARE(virtuality, PureVirtual); + else + QCOMPARE(virtuality, Virtual); + } else { + QEXPECT_FAIL("virtual-dtor-dtor", "Not implemented", Abort); + if (allFunctions.size() == 3) + QEXPECT_FAIL("dtor-virtual-dtor-dtor", "Not implemented", Abort); + QCOMPARE(virtuality, NotVirtual); + } + if (firstVirtualIndex == -1) + QVERIFY(!firstVirtual); + else + QCOMPARE(firstVirtual, allFunctions.at(firstVirtualIndex)); + } + } + QVERIFY(virtualityList.isEmpty()); + QVERIFY(firstVirtualList.isEmpty()); +} + +void CppToolsPlugin::test_functionutils_virtualFunctions_data() +{ + typedef QByteArray _; + QTest::addColumn<QByteArray>("source"); + QTest::addColumn<VirtualityList>("virtualityList"); + QTest::addColumn<QList<int> >("firstVirtualList"); + + QTest::newRow("none") + << _("struct None { void foo() {} };\n") + << (VirtualityList() << NotVirtual) + << (QList<int>() << -1); + + QTest::newRow("single-virtual") + << _("struct V { virtual void foo() {} };\n") + << (VirtualityList() << Virtual) + << (QList<int>() << 0); + + QTest::newRow("single-pure-virtual") + << _("struct PV { virtual void foo() = 0; };\n") + << (VirtualityList() << PureVirtual) + << (QList<int>() << 0); + + QTest::newRow("virtual-derived-with-specifier") + << _("struct Base { virtual void foo() {} };\n" + "struct Derived : Base { virtual void foo() {} };\n") + << (VirtualityList() << Virtual << Virtual) + << (QList<int>() << 0 << 0); + + QTest::newRow("virtual-derived-implicit") + << _("struct Base { virtual void foo() {} };\n" + "struct Derived : Base { void foo() {} };\n") + << (VirtualityList() << Virtual << Virtual) + << (QList<int>() << 0 << 0); + + QTest::newRow("not-virtual-then-virtual") + << _("struct Base { void foo() {} };\n" + "struct Derived : Base { virtual void foo() {} };\n") + << (VirtualityList() << NotVirtual << Virtual) + << (QList<int>() << -1 << 1); + + QTest::newRow("virtual-final-not-virtual") + << _("struct Base { virtual void foo() {} };\n" + "struct Derived : Base { void foo() final {} };\n" + "struct Derived2 : Derived { void foo() {} };") + << (VirtualityList() << Virtual << Virtual << NotVirtual) + << (QList<int>() << 0 << 0 << -1); + + QTest::newRow("virtual-then-pure") + << _("struct Base { virtual void foo() {} };\n" + "struct Derived : Base { virtual void foo() = 0; };\n" + "struct Derived2 : Derived { void foo() {} };") + << (VirtualityList() << Virtual << PureVirtual << Virtual) + << (QList<int>() << 0 << 0 << 0); + + QTest::newRow("virtual-virtual-final-not-virtual") + << _("struct Base { virtual void foo() {} };\n" + "struct Derived : Base { virtual void foo() final {} };\n" + "struct Derived2 : Derived { void foo() {} };") + << (VirtualityList() << Virtual << Virtual << NotVirtual) + << (QList<int>() << 0 << 0 << -1); + + QTest::newRow("ctor-virtual-dtor") + << _("struct Base { Base() {} virtual ~Base() {} };\n") + << (VirtualityList() << NotVirtual << Virtual) + << (QList<int>() << -1 << 1); + + QTest::newRow("virtual-dtor-dtor") + << _("struct Base { virtual ~Base() {} };\n" + "struct Derived : Base { ~Derived() {} };\n") + << (VirtualityList() << Virtual << Virtual) + << (QList<int>() << 0 << 0); + + QTest::newRow("dtor-virtual-dtor-dtor") + << _("struct Base { ~Base() {} };\n" + "struct Derived : Base { virtual ~Derived() {} };\n" + "struct Derived2 : Derived { ~Derived2() {} };\n") + << (VirtualityList() << NotVirtual << Virtual << Virtual) + << (QList<int>() << -1 << 1 << 1); +} + +} // namespace Internal +} // namespace CppTools + +#endif |