/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** 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 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.
**
****************************************************************************/

#include "cppeditor.h"
#include "cppeditorwidget.h"
#include "cppeditorplugin.h"
#include "cppeditortestcase.h"

#include <cpptools/cppelementevaluator.h>
#include <cpptools/cppfollowsymbolundercursor.h>
#include <cpptools/cppvirtualfunctionassistprovider.h>
#include <cpptools/cppvirtualfunctionproposalitem.h>
#include <cpptools/cpptoolstestcase.h>
#include <cpptools/cppmodelmanager.h>

#include <texteditor/codeassist/genericproposalmodel.h>
#include <texteditor/codeassist/iassistprocessor.h>
#include <texteditor/codeassist/iassistproposal.h>

#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/idocument.h>

#include <utils/fileutils.h>

#include <QDebug>
#include <QDir>
#include <QtTest>

//
// The following "non-latin1" code points are used in the tests:
//
//   U+00FC  - 2 code units in UTF8, 1 in UTF16 - LATIN SMALL LETTER U WITH DIAERESIS
//   U+4E8C  - 3 code units in UTF8, 1 in UTF16 - CJK UNIFIED IDEOGRAPH-4E8C
//   U+10302 - 4 code units in UTF8, 2 in UTF16 - OLD ITALIC LETTER KE
//

#define UNICODE_U00FC "\xc3\xbc"
#define UNICODE_U4E8C "\xe4\xba\x8c"
#define UNICODE_U10302 "\xf0\x90\x8c\x82"
#define TEST_UNICODE_IDENTIFIER UNICODE_U00FC UNICODE_U4E8C UNICODE_U10302

/*!
    Tests for Follow Symbol Under Cursor and Switch Between Function Declaration/Definition

    Section numbers refer to

        Working Draft, Standard for Programming Language C++
        Document Number: N3242=11-0012

    You can find potential test code for Follow Symbol Under Cursor on the bottom of this file.
 */

using namespace CPlusPlus;
using namespace CppTools;
using namespace TextEditor;
using namespace Core;

class OverrideItem {
public:
    OverrideItem() : line(0) {}
    OverrideItem(const QString &text, int line = 0) : text(text), line(line) {}
    bool isValid() { return line != 0; }
    QByteArray toByteArray() const
    {
        return "OverrideItem(" + text.toLatin1() + ", " + QByteArray::number(line) + ')';
    }

    QString text;
    int line;
};
typedef QList<OverrideItem> OverrideItemList;
Q_DECLARE_METATYPE(OverrideItem)

inline bool operator==(const OverrideItem &lhs, const OverrideItem &rhs)
{
    return lhs.text == rhs.text && lhs.line == rhs.line;
}

QT_BEGIN_NAMESPACE
namespace QTest {
template<> char *toString(const OverrideItem &data)
{
    return qstrdup(data.toByteArray().constData());
}
}

QDebug &operator<<(QDebug &d, const OverrideItem &data)
{
    d << data.toByteArray();
    return d;
}
QT_END_NAMESPACE

typedef QByteArray _;

namespace CppEditor {
namespace Internal {

/// A fake virtual functions assist provider that runs processor->perform() already in configure()
class VirtualFunctionTestAssistProvider : public VirtualFunctionAssistProvider
{
public:
    VirtualFunctionTestAssistProvider(CppEditorWidget *editorWidget)
        : m_editorWidget(editorWidget)
    {}

    // Invoke the processor already here to calculate the proposals. Return false in order to
    // indicate that configure failed, so the actual code assist invocation leading to a pop-up
    // will not happen.
    bool configure(const VirtualFunctionAssistProvider::Parameters &params) override
    {
        VirtualFunctionAssistProvider::configure(params);

        const QScopedPointer<IAssistProcessor> processor(createProcessor());
        AssistInterface *assistInterface
                = m_editorWidget->createAssistInterface(FollowSymbol, ExplicitlyInvoked);

        const QScopedPointer<IAssistProposal> immediateProposal(
            processor->immediateProposal(assistInterface));
        const QScopedPointer<IAssistProposal> finalProposal(processor->perform(assistInterface));

        VirtualFunctionAssistProvider::clearParams();

        m_immediateItems = itemList(immediateProposal->model());
        m_finalItems = itemList(finalProposal->model());

        return false;
    }

    static OverrideItemList itemList(ProposalModelPtr imodel)
    {
        OverrideItemList result;
        GenericProposalModelPtr model = imodel.staticCast<GenericProposalModel>();
        if (!model)
            return result;

        // Mimic relevant GenericProposalWidget::showProposal() calls
        model->removeDuplicates();
        model->reset();
        if (model->isSortable(QString()))
            model->sort(QString());

        for (int i = 0, size = model->size(); i < size; ++i) {
            VirtualFunctionProposalItem *item
                = dynamic_cast<VirtualFunctionProposalItem *>(model->proposalItem(i));

            const QString text = model->text(i);
            const int line = item->link().targetLine;
//            Uncomment for updating/generating reference data:
//            qDebug("<< OverrideItem(QLatin1String(\"%s\"), %d)", qPrintable(text), line);
            result << OverrideItem(text, line);
        }

        return result;
    }

public:
    OverrideItemList m_immediateItems;
    OverrideItemList m_finalItems;

private:
    CppEditorWidget *m_editorWidget;
};

class TestDocument;
typedef QSharedPointer<TestDocument> TestDocumentPtr;

/**
 * Represents a test document.
 *
 * A TestDocument's source can contain special characters:
 *   - a '@' character denotes the initial text cursor position
 *   - a '$' character denotes the target text cursor position
 */
class TestDocument : public Tests::TestDocument
{
public:
    TestDocument(const QByteArray &source, const QByteArray &fileName)
        : Tests::TestDocument(fileName, source)
        , m_targetCursorPosition(m_source.indexOf(QLatin1Char('$')))
    {
        if (m_cursorPosition != -1 || m_targetCursorPosition != -1)
            QVERIFY(m_cursorPosition != m_targetCursorPosition);

        if (m_cursorPosition > m_targetCursorPosition) {
            m_source.remove(m_cursorPosition, 1);
            if (m_targetCursorPosition != -1) {
                m_source.remove(m_targetCursorPosition, 1);
                --m_cursorPosition;
            }
        } else {
            m_source.remove(m_targetCursorPosition, 1);
            if (m_cursorPosition != -1) {
                m_source.remove(m_cursorPosition, 1);
                --m_targetCursorPosition;
            }
        }
    }

    static TestDocumentPtr create(const QByteArray &source, const QByteArray &fileName)
    {
        return TestDocumentPtr(new TestDocument(source, fileName));
    }

    bool hasTargetCursorMarker() const { return m_targetCursorPosition != -1; }

public:
    int m_targetCursorPosition;
};

QList<TestDocumentPtr> singleDocument(const QByteArray &source)
{
    return QList<TestDocumentPtr>() << TestDocument::create(source, "file.cpp");
}

/**
 * Encapsulates the whole process of setting up several editors, positioning the cursor,
 * executing Follow Symbol Under Cursor or Switch Between Function Declaration/Definition
 * and checking the result.
 */
class F2TestCase : public Tests::TestCase
{
public:
    enum CppEditorAction {
        FollowSymbolUnderCursorAction,
        SwitchBetweenMethodDeclarationDefinitionAction
    };

    F2TestCase(CppEditorAction action,
               const QList<TestDocumentPtr> &testFiles,
               const OverrideItemList &expectedVirtualFunctionProposal = OverrideItemList());

private:
    static TestDocumentPtr testFileWithInitialCursorMarker(const QList<TestDocumentPtr> &testFiles);
    static TestDocumentPtr testFileWithTargetCursorMarker(const QList<TestDocumentPtr> &testFiles);
};

/// Creates a test case with multiple test files.
/// Exactly one test document must be provided that contains '@', the initial position marker.
/// Exactly one test document must be provided that contains '$', the target position marker.
/// It can be the same document.
F2TestCase::F2TestCase(CppEditorAction action,
                       const QList<TestDocumentPtr> &testFiles,
                       const OverrideItemList &expectedVirtualFunctionProposal)
{
    QVERIFY(succeededSoFar());

    // Check if there are initial and target position markers
    TestDocumentPtr initialTestFile = testFileWithInitialCursorMarker(testFiles);
    QVERIFY2(initialTestFile,
        "No test file with initial cursor marker is provided.");
    TestDocumentPtr targetTestFile = testFileWithTargetCursorMarker(testFiles);
    QVERIFY2(targetTestFile,
        "No test file with target cursor marker is provided.");

    // Write files to disk
    CppTools::Tests::TemporaryDir temporaryDir;
    QVERIFY(temporaryDir.isValid());
    foreach (TestDocumentPtr testFile, testFiles) {
        QVERIFY(testFile->baseDirectory().isEmpty());
        testFile->setBaseDirectory(temporaryDir.path());
        QVERIFY(testFile->writeToDisk());
    }

    // Update Code Model
    QSet<QString> filePaths;
    foreach (const TestDocumentPtr &testFile, testFiles)
        filePaths << testFile->filePath();
    QVERIFY(parseFiles(filePaths));

    // Open Files
    foreach (TestDocumentPtr testFile, testFiles) {
        QVERIFY(openCppEditor(testFile->filePath(), &testFile->m_editor,
                              &testFile->m_editorWidget));
        closeEditorAtEndOfTestCase(testFile->m_editor);

        // Wait until the indexer processed the just opened file.
        // The file is "Full Checked" since it is in the working copy now,
        // that is the function bodies are processed.
        forever {
            const Document::Ptr document = waitForFileInGlobalSnapshot(testFile->filePath());
            QVERIFY(document);
            if (document->checkMode() == Document::FullCheck) {
                QVERIFY(document->diagnosticMessages().isEmpty());
                break;
            }
        }

        // Rehighlight
        waitForRehighlightedSemanticDocument(testFile->m_editorWidget);
    }

    // Activate editor of initial test file
    EditorManager::activateEditor(initialTestFile->m_editor);

    initialTestFile->m_editor->setCursorPosition(initialTestFile->m_cursorPosition);
//    qDebug() << "Initial line:" << initialTestFile->editor->currentLine();
//    qDebug() << "Initial column:" << initialTestFile->editor->currentColumn();

    OverrideItemList immediateVirtualSymbolResults;
    OverrideItemList finalVirtualSymbolResults;

    // Trigger the action
    switch (action) {
    case FollowSymbolUnderCursorAction: {
        CppEditorWidget *widget = initialTestFile->m_editorWidget;
        FollowSymbolInterface &delegate = CppModelManager::instance()->followSymbolInterface();
        auto* builtinFollowSymbol = dynamic_cast<FollowSymbolUnderCursor *>(&delegate);
        if (!builtinFollowSymbol) {
            if (filePaths.size() > 1)
                QSKIP("Clang FollowSymbol does not currently support multiple files (except cpp+header)");
            const QString curTestName = QLatin1String(QTest::currentTestFunction());
            if (curTestName == "test_FollowSymbolUnderCursor_QObject_connect"
                    || curTestName == "test_FollowSymbolUnderCursor_virtualFunctionCall"
                    || curTestName == "test_FollowSymbolUnderCursor_QTCREATORBUG7903") {
                QSKIP((curTestName + " is not supported by Clang FollowSymbol").toLatin1());
            }

            widget->openLinkUnderCursor();
            break;
        }

        QSharedPointer<VirtualFunctionAssistProvider> original
                = builtinFollowSymbol->virtualFunctionAssistProvider();

        // Set test provider, run and get results
        QSharedPointer<VirtualFunctionTestAssistProvider> testProvider(
            new VirtualFunctionTestAssistProvider(widget));
        builtinFollowSymbol->setVirtualFunctionAssistProvider(testProvider);
        widget->openLinkUnderCursor();
        immediateVirtualSymbolResults = testProvider->m_immediateItems;
        finalVirtualSymbolResults = testProvider->m_finalItems;

        // Restore original test provider
        builtinFollowSymbol->setVirtualFunctionAssistProvider(original);
        break;
    }
    case SwitchBetweenMethodDeclarationDefinitionAction:
        CppEditorPlugin::instance()->switchDeclarationDefinition();
        break;
    default:
        QFAIL("Unknown test action");
        break;
    }

    QCoreApplication::processEvents();

    // Compare
    IEditor *currentEditor = EditorManager::currentEditor();
    BaseTextEditor *currentTextEditor = dynamic_cast<BaseTextEditor*>(currentEditor);
    QVERIFY(currentTextEditor);

    QCOMPARE(currentTextEditor->document()->filePath().toString(), targetTestFile->filePath());
    int expectedLine, expectedColumn;
    currentTextEditor->convertPosition(targetTestFile->m_targetCursorPosition,
                                       &expectedLine, &expectedColumn);
//    qDebug() << "Expected line:" << expectedLine;
//    qDebug() << "Expected column:" << expectedColumn;

    QEXPECT_FAIL("globalVarFromEnum", "Contributor works on a fix.", Abort);
    QEXPECT_FAIL("matchFunctionSignature_Follow_5", "foo(int) resolved as CallAST", Abort);
    QCOMPARE(currentTextEditor->currentLine(), expectedLine);
    QCOMPARE(currentTextEditor->currentColumn(), expectedColumn);

//    qDebug() << immediateVirtualSymbolResults;
//    qDebug() << finalVirtualSymbolResults;
    OverrideItemList expectedImmediate;
    if (!expectedVirtualFunctionProposal.isEmpty()) {
        expectedImmediate << expectedVirtualFunctionProposal.first();
        expectedImmediate << OverrideItem(QLatin1String("...searching overrides"));
    }
    QCOMPARE(immediateVirtualSymbolResults, expectedImmediate);
    QCOMPARE(finalVirtualSymbolResults, expectedVirtualFunctionProposal);
}

TestDocumentPtr F2TestCase::testFileWithInitialCursorMarker(const QList<TestDocumentPtr> &testFiles)
{
    foreach (const TestDocumentPtr testFile, testFiles) {
        if (testFile->hasCursorMarker())
            return testFile;
    }
    return TestDocumentPtr();
}

TestDocumentPtr F2TestCase::testFileWithTargetCursorMarker(const QList<TestDocumentPtr> &testFiles)
{
    foreach (const TestDocumentPtr testFile, testFiles) {
        if (testFile->hasTargetCursorMarker())
            return testFile;
    }
    return TestDocumentPtr();
}

} // namespace Internal
} // namespace CppEditor

Q_DECLARE_METATYPE(QList<CppEditor::Internal::TestDocumentPtr>)

namespace CppEditor {
namespace Internal {

void CppEditorPlugin::test_SwitchMethodDeclarationDefinition_data()
{
    QTest::addColumn<QByteArray>("header");
    QTest::addColumn<QByteArray>("source");

    QTest::newRow("fromFunctionDeclarationSymbol") << _(
        "class C\n"
        "{\n"
        "public:\n"
        "    C();\n"
        "    int @function();\n"  // Line 5
        "};\n"
        ) << _(
        "#include \"file.h\"\n"
        "\n"
        "C::C()\n"
        "{\n"
        "}\n"                   // Line 5
        "\n"
        "int C::$function()\n"
        "{\n"
        "    return 1 + 1;\n"
        "}\n"                   // Line 10
    );

    QTest::newRow("fromFunctionDefinitionSymbol") << _(
        "class C\n"
        "{\n"
        "public:\n"
        "    C();\n"
        "    int $function();\n"
        "};\n"
        ) << _(
        "#include \"file.h\"\n"
        "\n"
        "C::C()\n"
        "{\n"
        "}\n"
        "\n"
        "int C::@function()\n"
        "{\n"
        "    return 1 + 1;\n"
        "}\n"
    );

    QTest::newRow("fromFunctionBody") << _(
        "class C\n"
        "{\n"
        "public:\n"
        "    C();\n"
        "    int $function();\n"
        "};\n"
        ) << _(
        "#include \"file.h\"\n"
        "\n"
        "C::C()\n"
        "{\n"
        "}\n"                   // Line 5
        "\n"
        "int C::function()\n"
        "{\n"
        "    return @1 + 1;\n"
        "}\n"                   // Line 10
    );

    QTest::newRow("fromReturnType") << _(
        "class C\n"
        "{\n"
        "public:\n"
        "    C();\n"
        "    int $function();\n"
        "};\n"
        ) << _(
        "#include \"file.h\"\n"
        "\n"
        "C::C()\n"
        "{\n"
        "}\n"                   // Line 5
        "\n"
        "@int C::function()\n"
        "{\n"
        "    return 1 + 1;\n"
        "}\n"                   // Line 10
    );

    QTest::newRow("matchFunctionSignature_Def_1") << _(
        "class Foo {\n"
        "    void @foo(int);\n"
        "    void foo();\n"
        "};\n"
        ) << _(
        "#include \"file.h\"\n"
        "void Foo::$foo(int) {}\n"
    );

    QTest::newRow("matchFunctionSignature_Def_2") << _(
        "class Foo {\n"
        "    void $foo(int);\n"
        "    void foo();\n"
        "};\n"
        ) << _(
        "#include \"file.h\"\n"
        "void Foo::@foo(int) {}\n"
    );

    QTest::newRow("matchFunctionSignature_Def_3") << _(
        "class Foo {\n"
        "    void foo(int);\n"
        "    void @$foo() {}\n"
        "};\n"
        ) << _(
        "#include \"file.h\"\n"
        "void Foo::foo(int) {}\n"
    );

    QTest::newRow("matchFunctionSignature_Def_4") << _(
        "class Foo {\n"
        "    void foo(int);\n"
        "    void @$foo();\n"
        "};\n"
    ) << _();

    QTest::newRow("matchFunctionSignature_Def_5") << _(
        "class Foo {\n"
        "    void @$foo(int);\n"
        "    void foo();\n"
        "};\n"
    ) << _();

    QTest::newRow("unicodeIdentifier") << _(
        "class Foo { void $" TEST_UNICODE_IDENTIFIER "(); };\n"
        "void Foo::@" TEST_UNICODE_IDENTIFIER "() {}\n"
    ) << _();

    QTest::newRow("globalVar")
            << _("namespace NS { extern int @globalVar; }\n")
            << _("int globalVar;\n"
                 "namespace NS {\n"
                 "extern int globalVar;\n"
                 "int $globalVar;\n"
                 "}\n");

    QTest::newRow("staticMemberVar")
            << _("namespace NS {\n"
                 "class Test {\n"
                 "    static int @var;\n"
                 "};\n"
                 "class OtherClass { static int var; };\n"
                 "}\n"
                 "class OtherClass { static int var; };\n")
            << _("#include \"file.h\"\n"
                 "int var;\n"
                 "int OtherClass::var;\n"
                 "namespace NS {\n"
                 "int OtherClass::var;\n"
                 "float Test::var;\n"
                 "int Test::$var;\n"
                 "}\n");
}

void CppEditorPlugin::test_SwitchMethodDeclarationDefinition()
{
    QFETCH(QByteArray, header);
    QFETCH(QByteArray, source);

    const QList<TestDocumentPtr> testFiles = QList<TestDocumentPtr>()
        << TestDocument::create(header, "file.h")
        << TestDocument::create(source, "file.cpp");

    F2TestCase(F2TestCase::SwitchBetweenMethodDeclarationDefinitionAction, testFiles);
}

void CppEditorPlugin::test_FollowSymbolUnderCursor_data()
{
    QTest::addColumn<QByteArray>("source");

    /// Check ...
    QTest::newRow("globalVarFromFunction") << _(
        "int $j;\n"
        "int main()\n"
        "{\n"
        "    @j = 2;\n"
        "}\n"           // Line 5
    );

    // 3.3.10 Name hiding (par 3.), from text
    QTest::newRow("funLocalVarHidesClassMember") << _(
        "struct C {\n"
        "    void f()\n"
        "    {\n"
        "        int $member; // hides C::member\n"
        "        ++@member;\n"                       // Line 5
        "    }\n"
        "    int member;\n"
        "};\n"
    );

    // 3.3.10 Name hiding (par 4.), from text
    QTest::newRow("funLocalVarHidesNamespaceMemberIntroducedByUsingDirective") << _(
        "namespace N {\n"
        "    int i;\n"
        "}\n"
        "\n"
        "int main()\n"                       // Line 5
        "{\n"
        "    using namespace N;\n"
        "    int $i;\n"
        "    ++i@; // refers to local i;\n"
        "}\n"                                // Line 10
    );

    // 3.3.3 Block scope (par. 4), from text
    // Same for if, while, switch
    QTest::newRow("loopLocalVarHidesOuterScopeVariable1") << _(
        "int main()\n"
        "{\n"
        "    int i = 1;\n"
        "    for (int $i = 0; i < 10; ++i) { // 'i' refers to for's i\n"
        "        i = @i; // same\n"                                       // Line 5
        "    }\n"
        "}\n"
    );

    // 3.3.3 Block scope (par. 4), from text
    // Same for if, while, switch
    QTest::newRow("loopLocalVarHidesOuterScopeVariable2") << _(
        "int main()\n"
        "{\n"
        "    int i = 1;\n"
        "    for (int $i = 0; @i < 10; ++i) { // 'i' refers to for's i\n"
        "        i = i; // same\n"                                         // Line 5
        "    }\n"
        "}\n"
    );

    // 3.3.7 Class scope, part of the example
    QTest::newRow("subsequentDefinedClassMember") << _(
        "class X {\n"
        "    int f() { return @i; } // i refers to class's i\n"
        "    int $i;\n"
        "};\n"
    );

    // 3.3.7 Class scope, part of the example
    // Variable name hides type name.
    QTest::newRow("classMemberHidesOuterTypeDef") << _(
        "typedef int c;\n"
        "class X {\n"
        "    int f() { return @c; } // c refers to class' c\n"
        "    int $c; // hides typedef name\n"
        "};\n"                                                 // Line 5
    );

    // 3.3.2 Point of declaration (par. 1), copy-paste
    QTest::newRow("globalVarFromEnum") << _(
        "const int $x = 12;\n"
        "int main()\n"
        "{\n"
        "    enum { x = @x }; // x refers to global x\n"
        "}\n"                                             // Line 5
    );

    // 3.3.2 Point of declaration
    QTest::newRow("selfInitialization") << _(
        "int x = 12;\n"
        "int main()\n"
        "{\n"
        "   int $x = @x; // Second x refers to local x\n"
        "}\n"                                              // Line 5
    );

    // 3.3.2 Point of declaration (par. 3), from text
    QTest::newRow("pointerToClassInClassDefinition") << _(
        "class $Foo {\n"
        "    @Foo *p; // Refers to above Foo\n"
        "};\n"
    );

    // 3.3.2 Point of declaration (par. 5), copy-paste
    QTest::newRow("previouslyDefinedMemberFromArrayDefinition") << _(
        "struct X {\n"
        "    enum E { $z = 16 };\n"
        "    int b[X::@z]; // z refers to defined z\n"
        "};\n"
    );

    // 3.3.7 Class scope (par. 2), from text
    QTest::newRow("outerStaticMemberVariableFromInsideSubclass") << _(
        "struct C\n"
        "{\n"
        "   struct I\n"
        "   {\n"
        "       void f()\n"                            // Line 5
        "       {\n"
        "           int i = @c; // refers to C's c\n"
        "       }\n"
        "   };\n"
        "\n"                                           // Line 10
        "   static int $c;\n"
        "};\n"
    );

    // 3.3.7 Class scope (par. 1), part of point 5
    QTest::newRow("memberVariableFollowingDotOperator") << _(
        "struct C\n"
        "{\n"
        "    int $member;\n"
        "};\n"
        "\n"                 // Line 5
        "int main()\n"
        "{\n"
        "    C c;\n"
        "    c.@member++;\n"
        "}\n"                // Line 10
    );

    // 3.3.7 Class scope (par. 1), part of point 5
    QTest::newRow("memberVariableFollowingArrowOperator") << _(
        "struct C\n"
        "{\n"
        "    int $member;\n"
        "};\n"
        "\n"                    // Line 5
        "int main()\n"
        "{\n"
        "    C* c;\n"
        "    c->@member++;\n"
        "}\n"                   // Line 10
    );

    // 3.3.7 Class scope (par. 1), part of point 5
    QTest::newRow("staticMemberVariableFollowingScopeOperator") << _(
        "struct C\n"
        "{\n"
        "    static int $member;\n"
        "};\n"
        "\n"                        // Line 5
        "int main()\n"
        "{\n"
        "    C::@member++;\n"
        "}\n"
    );

    // 3.3.7 Class scope (par. 2), from text
    QTest::newRow("staticMemberVariableFollowingDotOperator") << _(
        "struct C\n"
        "{\n"
        "    static int $member;\n"
        "};\n"
        "\n"                        // Line 5
        "int main()\n"
        "{\n"
        "    C c;\n"
        "    c.@member;\n"
        "}\n"                       // Line 10
    );


    // 3.3.7 Class scope (par. 2), from text
    QTest::newRow("staticMemberVariableFollowingArrowOperator") << _(
        "struct C\n"
        "{\n"
        "    static int $member;\n"
        "};\n"
        "\n"                         // Line 5
        "int main()\n"
        "{\n"
        "    C *c;\n"
        "    c->@member++;\n"
        "}\n"                        // Line 10
    );

    // 3.3.8 Enumeration scope
    QTest::newRow("previouslyDefinedEnumValueFromInsideEnum") << _(
        "enum {\n"
        "    $i = 0,\n"
        "    j = @i // refers to i above\n"
        "};\n"
    );

    // 3.3.8 Enumeration scope
    QTest::newRow("nsMemberHidesNsMemberIntroducedByUsingDirective") << _(
        "namespace A {\n"
        "    char x;\n"
        "}\n"
        "\n"
        "namespace B {\n"                               // Line 5
        "    using namespace A;\n"
        "    int $x; // hides A::x\n"
        "}\n"
        "\n"
        "int main()\n"                                  // Line 10
        "{\n"
        "    B::@x++; // refers to B's X, not A::x\n"
        "}\n"
    );

    // 3.3.10 Name hiding, from text
    // www.stroustrup.com/bs_faq2.html#overloadderived
    QTest::newRow("baseClassFunctionIntroducedByUsingDeclaration") << _(
        "struct B {\n"
        "    int $f(int) {}\n"
        "};\n"
        "\n"
        "class D : public B {\n"                              // Line 5
        "public:\n"
        "    using B::f; // make every f from B available\n"
        "    double f(double) {}\n"
        "};\n"
        "\n"                                                  // Line 10
        "int main()\n"
        "{\n"
        "    D* pd = new D;\n"
        "    pd->@f(2); // refers to B::f\n"
        "    pd->f(2.3); // refers to D::f\n"                 // Line 15
        "}\n"
    );

    // 3.3.10 Name hiding, from text
    // www.stroustrup.com/bs_faq2.html#overloadderived
    QTest::newRow("funWithSameNameAsBaseClassFunIntroducedByUsingDeclaration") << _(
        "struct B {\n"
        "    int f(int) {}\n"
        "};\n"
        "\n"
        "class D : public B {\n"                              // Line 5
        "public:\n"
        "    using B::f; // make every f from B available\n"
        "    double $f(double) {}\n"
        "};\n"
        "\n"                                                  // Line 10
        "int main()\n"
        "{\n"
        "    D* pd = new D;\n"
        "    pd->f(2); // refers to B::f\n"
        "    pd->@f(2.3); // refers to D::f\n"                // Line 15
        "}\n"
    );

    // 3.3.10 Name hiding (par 2.), from text
    // A class name (9.1) or enumeration name (7.2) can be hidden by the name of a variable,
    // data member, function, or enumerator declared in the same scope.
    QTest::newRow("funLocalVarHidesOuterClass") << _(
        "struct C {};\n"
        "\n"
        "int main()\n"
        "{\n"
        "    int $C; // hides type C\n"  // Line 5
        "    ++@C;\n"
        "}\n"
    );

    QTest::newRow("classConstructor") << _(
        "class Foo {\n"
        "    F@oo();"
        "    ~Foo();"
        "};\n\n"
        "Foo::$Foo()\n"
        "{\n"
        "}\n\n"
        "Foo::~Foo()\n"
        "{\n"
        "}\n"
    );

    QTest::newRow("classDestructor") << _(
        "class Foo {\n"
        "    Foo();"
        "    ~@Foo();"
        "};\n\n"
        "Foo::Foo()\n"
        "{\n"
        "}\n\n"
        "Foo::~$Foo()\n"
        "{\n"
        "}\n"
    );

    QTest::newRow("skipForwardDeclarationBasic") << _(
        "class $Foo {};\n"
        "class Foo;\n"
        "@Foo foo;\n"
    );

    QTest::newRow("skipForwardDeclarationTemplates") << _(
        "template <class E> class $Container {};\n"
        "template <class E> class Container;\n"
        "@Container<int> container;\n"
    );

    QTest::newRow("matchFunctionSignature_Follow_1") << _(
        "class Foo {\n"
        "    void @foo(int);\n"
        "    void foo();\n"
        "};\n"
        "void Foo::$foo(int) {}\n"
    );

    QTest::newRow("matchFunctionSignature_Follow_2") << _(
        "class Foo {\n"
        "    void $foo(int);\n"
        "    void foo();\n"
        "};\n"
        "void Foo::@foo(int) {}\n"
    );

    QTest::newRow("matchFunctionSignature_Follow_3") << _(
        "class Foo {\n"
        "    void foo(int);\n"
        "    void @$foo() {}\n"
        "};\n"
        "void Foo::foo(int) {}\n"
    );

    QTest::newRow("matchFunctionSignature_Follow_3.5") << _(
        "void foo(int);\n"
        "void @$foo() {}\n"
    );

    QTest::newRow("matchFunctionSignature_Follow_3.6") << _(
        "void foo(int);\n"
        "void @$foo(double) {}\n"
    );

    QTest::newRow("matchFunctionSignature_Follow_4") << _(
        "class Foo {\n"
        "    void foo(int);\n"
        "    void @$foo();\n"
        "};\n"
    );

    QTest::newRow("matchFunctionSignature_Follow_5") << _(
        "class Foo {\n"
        "    void @$foo(int);\n"
        "    void foo();\n"
        "};\n"
    );

    QTest::newRow("matchFunctionSignature_Follow_6") << _(
        "class Foo {\n"
        "    void $foo(int);\n"
        "};\n"
        "void Foo::@foo(const volatile int) {}\n"
    );

    QTest::newRow("matchFunctionSignature_Follow_7") << _(
        "class Foo {\n"
        "    void $foo(const volatile int);\n"
        "};\n"
        "void Foo::@foo(int) {}\n"
    );

    QTest::newRow("matchFunctionSignature_Follow_8_fuzzy") << _(
        "class Foo {\n"
        "    void @foo(int *);\n"
        "};\n"
        "void Foo::$foo(const int *) {}\n"
    );

    QTest::newRow("matchFunctionSignature_Follow_8_exact") << _(
        "class Foo {\n"
        "    void @foo(int *);\n"
        "};\n"
        "void Foo::foo(const int *) {}\n"
        "void Foo::$foo(int *) {}\n"
    );

    QTest::newRow("matchFunctionSignature_Follow_9_fuzzy") << _(
        "class Foo {\n"
        "    void @foo(int&);\n"
        "};\n"
        "void Foo::$foo(const int&) {}\n"
    );

    QTest::newRow("matchFunctionSignature_Follow_9_exact") << _(
        "class Foo {\n"
        "    void @foo(int&);\n"
        "};\n"
        "void Foo::$foo(int&) {}\n"
        "void Foo::foo(const int&) {}\n"
    );

    QTest::newRow("infiniteLoopLocalTypedef_QTCREATORBUG-11999") << _(
        "template<class MyTree>\n"
        "class TreeConstIterator\n"
        "{\n"
        "    typedef TreeConstIterator<MyTree> MyIter;\n"
        "    void f() { return this->@$g(); }\n"
        "};\n"
        "\n"
        "void h() { typedef TreeConstIterator<MyBase> const_iterator; }\n"
    );

    QTest::newRow("unicodeIdentifier") << _(
        "class Foo { void $" TEST_UNICODE_IDENTIFIER "(); };\n"
        "void Foo::@" TEST_UNICODE_IDENTIFIER "() {}\n"
    );

    QTest::newRow("trailingReturnType") << _(
        "struct $Foo {};\n"
        "auto foo() -> @Foo {}\n"
    );

    QTest::newRow("template_alias") << _(
        "template<class T>"
        "class Bar;"
        "template<class $T>\n"
        "using Foo = Bar<@T>;\n"
    );

    QTest::newRow("shadowed overload with default args") << _(
        "struct Parent { void disconnect(int n = 0); };\n"
        "struct Child : public Parent { void $disconnect(); };\n"
        "void test() { Child c; c.@disconnect(); }\n"
    );
}

void CppEditorPlugin::test_FollowSymbolUnderCursor()
{
    QFETCH(QByteArray, source);
    F2TestCase(F2TestCase::FollowSymbolUnderCursorAction, singleDocument(source));
}

void CppEditorPlugin::test_FollowSymbolUnderCursor_QTCREATORBUG7903_data()
{
    QTest::addColumn<QByteArray>("source");
    QTest::newRow("using_QTCREATORBUG7903_globalNamespace") << _(
        "namespace NS {\n"
        "class Foo {};\n"
        "}\n"
        "using NS::$Foo;\n"
        "void fun()\n"
        "{\n"
        "    @Foo foo;\n"
        "}\n"
    );

    QTest::newRow("using_QTCREATORBUG7903_namespace") << _(
        "namespace NS {\n"
        "class Foo {};\n"
        "}\n"
        "namespace NS1 {\n"
        "void fun()\n"
        "{\n"
        "    using NS::$Foo;\n"
        "    @Foo foo;\n"
        "}\n"
        "}\n"
    );

    QTest::newRow("using_QTCREATORBUG7903_insideFunction") << _(
        "namespace NS {\n"
        "class Foo {};\n"
        "}\n"
        "void fun()\n"
        "{\n"
        "    using NS::$Foo;\n"
        "    @Foo foo;\n"
        "}\n"
    );
}

void CppEditorPlugin::test_FollowSymbolUnderCursor_QTCREATORBUG7903()
{
    QFETCH(QByteArray, source);
    F2TestCase(F2TestCase::FollowSymbolUnderCursorAction, singleDocument(source));
}

void CppEditorPlugin::test_FollowSymbolUnderCursor_followCall_data()
{
    QTest::addColumn<QByteArray>("variableDeclaration"); // without semicolon, can be ""
    QTest::addColumn<QByteArray>("callArgument");
    QTest::addColumn<QByteArray>("expectedSignature"); // you might need to add a function
                                                       // declaration with such a signature

    QTest::newRow("intLiteral-to-int")
            << _("")
            << _("5")
            << _("int");
    QTest::newRow("charLiteral-to-const-char-ptr")
            << _("")
            << _("\"hoo\"")
            << _("const char *");
    QTest::newRow("charLiteral-to-int")
            << _("")
            << _("'a'")
            << _("char");

    QTest::newRow("charPtr-to-constCharPtr")
            << _("char *var = \"var\"")
            << _("var")
            << _("const char *");
    QTest::newRow("charPtr-to-constCharPtr")
            << _("char *var = \"var\"")
            << _("var")
            << _("const char *");
    QTest::newRow("constCharPtr-to-constCharPtr")
            << _("const char *var = \"var\"")
            << _("var")
            << _("const char *");

    QTest::newRow("Bar-to-constBarRef")
            << _("Bar var")
            << _("var")
            << _("const Bar &");
}

void CppEditorPlugin::test_FollowSymbolUnderCursor_followCall()
{
    QFETCH(QByteArray, variableDeclaration);
    QFETCH(QByteArray, callArgument);
    QFETCH(QByteArray, expectedSignature);

    const QByteArray templateSource =
        "class Bar {};\n"
        "void fun(int);\n"
        "void fun(const char *);\n"
        "void fun(const Bar &);\n"
        "void fun(char);\n"
        "void fun(double);\n"
        "\n"
        "void t()\n"
        "{\n"
        "   " + variableDeclaration + ";\n"
        "   @fun(" + callArgument + ");\n"
        "}\n";

    const QByteArray matchText = " fun(" + expectedSignature + ")";
    const QByteArray replaceText = " $fun(" + expectedSignature + ")";
    QByteArray source = templateSource;
    source.replace(matchText, replaceText);
    QVERIFY(source != templateSource);
    F2TestCase(F2TestCase::FollowSymbolUnderCursorAction, singleDocument(source));
}

void CppEditorPlugin::test_FollowSymbolUnderCursor_multipleDocuments_data()
{
    QTest::addColumn<QList<TestDocumentPtr> >("documents");

    QTest::newRow("skipForwardDeclarationBasic") << (QList<TestDocumentPtr>()
        << TestDocument::create("class $Foo {};\n",
                                "defined.h")
        << TestDocument::create("class Foo;\n"
                                "@Foo foo;\n",
                                "forwardDeclaredAndUsed.h")
    );

    QTest::newRow("skipForwardDeclarationTemplates") << (QList<TestDocumentPtr>()
        << TestDocument::create("template <class E> class $Container {};\n",
                                "defined.h")
        << TestDocument::create("template <class E> class Container;\n"
                                "@Container<int> container;\n",
                                "forwardDeclaredAndUsed.h")
    );

    QTest::newRow("matchFunctionSignature") << (QList<TestDocumentPtr>()
        << TestDocument::create("class Foo {\n"
                                "    void @foo(int);\n"
                                "    void foo() {}\n"
                                "};\n",
                                "foo.h")
        << TestDocument::create("#include \"foo.h\"\n"
                                "void Foo::$foo(int) {}\n",
                                "foo.cpp")
    );

    QTest::newRow("matchFunctionSignature2") << (QList<TestDocumentPtr>()
        << TestDocument::create("namespace N { class C; }\n"
                                "bool *@fun(N::C *) const;\n",
                                "foo.h")
        << TestDocument::create("#include \"foo.h\"\n"
                                "using namespace N;\n"
                                "bool *$fun(C *) const {}\n",
                                "foo.cpp")
    );

    QTest::newRow("matchFunctionSignatureFuzzy1Forward") << (QList<TestDocumentPtr>()
        << TestDocument::create("class Foo {\n"
                                "    void @foo(int);\n"
                                "    void foo();\n"
                                "};\n",
                                "foo.h")
        << TestDocument::create("#include \"foo.h\"\n"
                                "void Foo::$foo() {}\n",
                                "foo.cpp")
    );

    QTest::newRow("matchFunctionSignatureFuzzy1Backward") << (QList<TestDocumentPtr>()
        << TestDocument::create("class Foo {\n"
                                "    void $foo(int);\n"
                                "};\n",
                                "foo.h")
        << TestDocument::create("#include \"foo.h\"\n"
                                "void Foo::@foo() {}\n",
                                "foo.cpp")
    );

    QTest::newRow("matchFunctionSignatureFuzzy2Forward") << (QList<TestDocumentPtr>()
        << TestDocument::create("class Foo {\n"
                                "    void foo(int);\n"
                                "    void @foo();\n"
                                "};\n",
                                "foo.h")
        << TestDocument::create("#include \"foo.h\"\n"
                                "void Foo::$foo(int) {}\n",
                                "foo.cpp")
    );

    QTest::newRow("matchFunctionSignatureFuzzy2Backward") << (QList<TestDocumentPtr>()
        << TestDocument::create("class Foo {\n"
                                "    void $foo();\n"
                                "};\n",
                                "foo.h")
        << TestDocument::create("#include \"foo.h\"\n"
                                "void Foo::@foo(int) {}\n",
                                "foo.cpp")
    );

    QTest::newRow("globalVar") << QList<TestDocumentPtr>{
            TestDocument::create("namespace NS { extern int @globalVar; }\n", "file.h"),
            TestDocument::create(
                "int globalVar;\n"
                "namespace NS {\n"
                "extern int globalVar;\n"
                "int $globalVar;\n"
                "}\n", "file.cpp")};

    QTest::newRow("staticMemberVar") << QList<TestDocumentPtr>{
        TestDocument::create(
            "namespace NS {\n"
            "class Test {\n"
            "    static int @var;\n"
            "};\n"
            "class OtherClass { static int var; };\n"
            "}\n"
            "class OtherClass { static int var; };\n", "file.h"),
        TestDocument::create(
            "#include \"file.h\"\n"
            "int var;\n"
            "int OtherClass::var;\n"
            "namespace NS {\n"
            "int OtherClass::var;\n"
            "float Test::var;\n"
            "int Test::$var;\n"
            "}\n", "file.cpp")};
}

void CppEditorPlugin::test_FollowSymbolUnderCursor_multipleDocuments()
{
    QFETCH(QList<TestDocumentPtr>, documents);
    F2TestCase(F2TestCase::FollowSymbolUnderCursorAction, documents);
}

void CppEditorPlugin::test_FollowSymbolUnderCursor_QObject_connect_data()
{
#define TAG(str) secondQObjectParam ? str : str ", no 2nd QObject"
    QTest::addColumn<char>("start");
    QTest::addColumn<char>("target");
    QTest::addColumn<bool>("secondQObjectParam");
    for (int i = 0; i < 2; ++i) {
        bool secondQObjectParam = (i == 0);
        QTest::newRow(TAG("SIGNAL: before keyword"))
                << '1' << '1' << secondQObjectParam;
        QTest::newRow(TAG("SIGNAL: in keyword"))
                << '2' << '1' << secondQObjectParam;
        QTest::newRow(TAG("SIGNAL: before parenthesis"))
                << '3' << '1' << secondQObjectParam;
        QTest::newRow(TAG("SIGNAL: before identifier"))
                << '4' << '1' << secondQObjectParam;
        QTest::newRow(TAG("SIGNAL: in identifier"))
                << '5' << '1' << secondQObjectParam;
        QTest::newRow(TAG("SIGNAL: before identifier parenthesis"))
                << '6' << '1' << secondQObjectParam;
        QTest::newRow(TAG("SLOT: before keyword"))
                << '7' << '2' << secondQObjectParam;
        QTest::newRow(TAG("SLOT: in keyword"))
                << '8' << '2' << secondQObjectParam;
        QTest::newRow(TAG("SLOT: before parenthesis"))
                << '9' << '2' << secondQObjectParam;
        QTest::newRow(TAG("SLOT: before identifier"))
                << 'A' << '2' << secondQObjectParam;
        QTest::newRow(TAG("SLOT: in identifier"))
                << 'B' << '2' << secondQObjectParam;
        QTest::newRow(TAG("SLOT: before identifier parenthesis"))
                << 'C' << '2' << secondQObjectParam;
    }
#undef TAG
}

static void selectMarker(QByteArray *source, char marker, char number)
{
    int idx = 0;
    forever {
        idx = source->indexOf(marker, idx);
        if (idx == -1)
            break;
        if (source->at(idx + 1) == number) {
            ++idx;
            source->remove(idx, 1);
        } else {
            source->remove(idx, 2);
        }
    }
}

void CppEditorPlugin::test_FollowSymbolUnderCursor_QObject_connect()
{
    QFETCH(char, start);
    QFETCH(char, target);
    QFETCH(bool, secondQObjectParam);
    QByteArray source =
            "#define QT_STRINGIFY2(x) #x\n"
            "#define QT_STRINGIFY(x) QT_STRINGIFY2(x)\n"
            "#define QLOCATION \"\\0\" __FILE__ \":\" QT_STRINGIFY(__LINE__)\n"
            "const char *qFlagLocation(const char *) { return 0; }\n"
            "#define SLOT(a) qFlagLocation(\"1\"#a QLOCATION)\n"
            "#define SIGNAL(a) qFlagLocation(\"2\"#a QLOCATION)\n"
            "\n"
            "#define slots\n"
            "#define signals public\n"
            "\n"
            "class QObject {};\n"
            "void connect(QObject *, const char *, QObject *, const char *) {}\n"
            "\n"
            "\n"
            "class Foo : public QObject\n"
            "{\n"
            "signals:\n"
            "    void $1endOfWorld();\n"
            "public slots:\n"
            "    void $2onWorldEnded()\n"
            "    {\n"
            "    }\n"
            "};\n"
            "\n"
            "void bla()\n"
            "{\n"
            "   Foo foo;\n"
            "   connect(&foo, @1SI@2GNAL@3(@4end@5OfWorld@6()),\n"
            "           &foo, @7SL@8OT@9(@Aon@BWorldEnded@C()));\n"
            "}\n";

    selectMarker(&source, '@', start);
    selectMarker(&source, '$', target);

    if (!secondQObjectParam)
        source.replace(" &foo, ", QByteArray());

    if (start >= '7' && !secondQObjectParam) {
        qWarning("SLOT jump triggers QTCREATORBUG-10265. Skipping.");
        return;
    }

    F2TestCase(F2TestCase::FollowSymbolUnderCursorAction, singleDocument(source));
}

void CppEditorPlugin::test_FollowSymbolUnderCursor_QObject_oldStyleConnect()
{
    const QByteArray source =
            "class O : public QObject {\n"
            "    Q_OBJECT\n"
            "signals:\n"
            "    void $theSignal();\n"
            "};\n"
            "struct S {\n"
            "    O* o();\n"
            "};\n"
            "int main()\n"
            "{\n"
            "    S s;\n"
            "    QObject::connect(s.o(), SIGNAL(@theSignal()), s.o(), SIGNAL(theSignal()));\n"
            "}\n";

    F2TestCase(F2TestCase::FollowSymbolUnderCursorAction, singleDocument(source));
}

void CppEditorPlugin::test_FollowSymbolUnderCursor_classOperator_onOperatorToken_data()
{
    QTest::addColumn<bool>("toDeclaration");
    QTest::newRow("forward") << false;
    QTest::newRow("backward") << true;
}

void CppEditorPlugin::test_FollowSymbolUnderCursor_classOperator_onOperatorToken()
{
    QFETCH(bool, toDeclaration);

    QByteArray source =
            "class Foo {\n"
            "    void @operator[](size_t idx);\n"
            "};\n\n"
            "void Foo::$operator[](size_t idx)\n"
            "{\n"
            "}\n";
    if (toDeclaration)
        source.replace('@', '#').replace('$', '@').replace('#', '$');
    F2TestCase(F2TestCase::FollowSymbolUnderCursorAction, singleDocument(source));
}

void CppEditorPlugin::test_FollowSymbolUnderCursor_classOperator_data()
{
    test_FollowSymbolUnderCursor_classOperator_onOperatorToken_data();
}

void CppEditorPlugin::test_FollowSymbolUnderCursor_classOperator()
{
    QFETCH(bool, toDeclaration);

    QByteArray source =
            "class Foo {\n"
            "    void $2operator@1[](size_t idx);\n"
            "};\n\n"
            "void Foo::$1operator@2[](size_t idx)\n"
            "{\n"
            "}\n";
    if (toDeclaration)
        source.replace("@1", QByteArray()).replace("$1", QByteArray())
                .replace("@2", "@").replace("$2", "$");
    else
        source.replace("@2", QByteArray()).replace("$2", QByteArray())
                .replace("@1", "@").replace("$1", "$");
    F2TestCase(F2TestCase::FollowSymbolUnderCursorAction, singleDocument(source));
}

void CppEditorPlugin::test_FollowSymbolUnderCursor_classOperator_inOp_data()
{
    test_FollowSymbolUnderCursor_classOperator_onOperatorToken_data();
}

void CppEditorPlugin::test_FollowSymbolUnderCursor_classOperator_inOp()
{
    QFETCH(bool, toDeclaration);

    QByteArray source =
            "class Foo {\n"
            "    void $2operator[@1](size_t idx);\n"
            "};\n\n"
            "void Foo::$1operator[@2](size_t idx)\n"
            "{\n"
            "}\n";
    if (toDeclaration)
        source.replace("@1", QByteArray()).replace("$1", QByteArray())
                .replace("@2", "@").replace("$2", "$");
    else
        source.replace("@2", QByteArray()).replace("$2", QByteArray())
                .replace("@1", "@").replace("$1", "$");
    F2TestCase(F2TestCase::FollowSymbolUnderCursorAction, singleDocument(source));
}

void CppEditorPlugin::test_FollowSymbolUnderCursor_virtualFunctionCall_data()
{
    QTest::addColumn<QByteArray>("source");
    QTest::addColumn<OverrideItemList>("results");

    /// Check: Static type is base class pointer, all overrides are presented.
    QTest::newRow("allOverrides") << _(
            "struct A { virtual void virt() = 0; };\n"
            "void A::virt() {}\n"
            "\n"
            "struct B : A { void virt(); };\n"
            "void B::virt() {}\n"
            "\n"
            "struct C : B { void virt(); };\n"
            "void C::virt() {}\n"
            "\n"
            "struct CD1 : C { void virt(); };\n"
            "void CD1::virt() {}\n"
            "\n"
            "struct CD2 : C { void virt(); };\n"
            "void CD2::virt() {}\n"
            "\n"
            "int f(A *o) { o->$@virt(); }\n"
            "\n")
        << (OverrideItemList()
            << OverrideItem(QLatin1String("A::virt = 0"), 2)
            << OverrideItem(QLatin1String("B::virt"), 5)
            << OverrideItem(QLatin1String("C::virt"), 8)
            << OverrideItem(QLatin1String("CD1::virt"), 11)
            << OverrideItem(QLatin1String("CD2::virt"), 14));

    /// Check: Cursor on unimplemented base declaration.
    QTest::newRow("allOverrides from base declaration") << _(
            "struct A { virtual void $@virt() = 0; };\n"
            "\n"
            "struct B : A { void virt(); };\n"
            "void B::virt() {}\n"
            "\n"
            "struct C : B { void virt(); };\n"
            "void C::virt() {}\n"
            "\n"
            "struct CD1 : C { void virt(); };\n"
            "void CD1::virt() {}\n"
            "\n"
            "struct CD2 : C { void virt(); };\n"
            "void CD2::virt() {}\n"
            "\n"
            "int f(A *o) { o->virt(); }\n"
            "\n")
        << (OverrideItemList()
            << OverrideItem(QLatin1String("A::virt = 0"), 1)
            << OverrideItem(QLatin1String("B::virt"), 4)
            << OverrideItem(QLatin1String("C::virt"), 7)
            << OverrideItem(QLatin1String("CD1::virt"), 10)
            << OverrideItem(QLatin1String("CD2::virt"), 13));

    /// Check: Static type is derived class pointer, only overrides of sub classes are presented.
    QTest::newRow("possibleOverrides1") << _(
            "struct A { virtual void virt() = 0; };\n"
            "void A::virt() {}\n"
            "\n"
            "struct B : A { void virt(); };\n"
            "void B::virt() {}\n"
            "\n"
            "struct C : B { void virt(); };\n"
            "void C::virt() {}\n"
            "\n"
            "struct CD1 : C { void virt(); };\n"
            "void CD1::virt() {}\n"
            "\n"
            "struct CD2 : C { void virt(); };\n"
            "void CD2::virt() {}\n"
            "\n"
            "int f(B *o) { o->$@virt(); }\n"
            "\n")
        << (OverrideItemList()
            << OverrideItem(QLatin1String("B::virt"), 5)
            << OverrideItem(QLatin1String("C::virt"), 8)
            << OverrideItem(QLatin1String("CD1::virt"), 11)
            << OverrideItem(QLatin1String("CD2::virt"), 14));

    /// Check: Virtual function call in member of class hierarchy,
    ///        only possible overrides are presented.
    QTest::newRow("possibleOverrides2") << _(
            "struct A { virtual void virt(); };\n"
            "void A::virt() {}\n"
            "\n"
            "struct B : public A { void virt(); };\n"
            "void B::virt() {}\n"
            "\n"
            "struct C : public B { void g() { virt$@(); } }; \n"
            "\n"
            "struct D : public C { void virt(); };\n"
            "void D::virt() {}\n")
        << (OverrideItemList()
            << OverrideItem(QLatin1String("B::virt"), 5)
            << OverrideItem(QLatin1String("D::virt"), 10));

    /// Check: If no definition is found, fallback to the declaration.
    QTest::newRow("fallbackToDeclaration") << _(
            "struct A { virtual void virt(); };\n"
            "\n"
            "int f(A *o) { o->$@virt(); }\n")
        << (OverrideItemList()
            << OverrideItem(QLatin1String("A::virt"), 1));

    /// Check: Ensure that the first entry in the final results is the same as the first in the
    ///        immediate results.
    QTest::newRow("itemOrder") << _(
            "struct C { virtual void virt() = 0; };\n"
            "void C::virt() {}\n"
            "\n"
            "struct B : C { void virt(); };\n"
            "void B::virt() {}\n"
            "\n"
            "struct A : B { void virt(); };\n"
            "void A::virt() {}\n"
            "\n"
            "int f(C *o) { o->$@virt(); }\n")
        << (OverrideItemList()
            << OverrideItem(QLatin1String("C::virt = 0"), 2)
            << OverrideItem(QLatin1String("A::virt"), 8)
            << OverrideItem(QLatin1String("B::virt"), 5));

    /// Check: If class templates are involved, the class and function symbols might be generated.
    ///        In that case, make sure that the symbols are not deleted before we reference them.
    QTest::newRow("instantiatedSymbols") << _(
            "template <class T> struct A { virtual void virt() {} };\n"
            "void f(A<int> *l) { l->$@virt(); }\n")
        << (OverrideItemList()
            << OverrideItem(QLatin1String("A::virt"), 1));

    /// Check: Static type is nicely resolved, especially for QSharedPointers.
    QTest::newRow("QSharedPointer") << _(
            "template <class T>\n"
            "class Basic\n"
            "{\n"
            "public:\n"
            "    inline T &operator*() const;\n"
            "    inline T *operator->() const;\n"
            "};\n"
            "\n"
            "template <class T> class ExternalRefCount: public Basic<T> {};\n"
            "template <class T> class QSharedPointer: public ExternalRefCount<T> {};\n"
            "\n"
            "struct A { virtual void virt() {} };\n"
            "struct B : public A { void virt() {} };\n"
            "\n"
            "int f()\n"
            "{\n"
            "    QSharedPointer<A> p(new A);\n"
            "    p->$@virt();\n"
            "}\n")
        << (OverrideItemList()
            << OverrideItem(QLatin1String("A::virt"), 12)
            << OverrideItem(QLatin1String("B::virt"), 13));

    /// Check: In case there is no override for the static type of a function call expression,
    ///        make sure to:
    ///         1) include the last provided override (look up bases)
    ///         2) and all overrides whose classes are derived from that static type
    QTest::newRow("noSiblings_references") << _(
            "struct A { virtual void virt(); };\n"
            "struct B : A { void virt() {} };\n"
            "struct C1 : B { void virt() {} };\n"
            "struct C2 : B { };\n"
            "struct D : C2 { void virt() {} };\n"
            "\n"
            "void f(C2 &o) { o.$@virt(); }\n")
        << (OverrideItemList()
            << OverrideItem(QLatin1String("B::virt"), 2)
            << OverrideItem(QLatin1String("D::virt"), 5));

    /// Variation of noSiblings_references
    QTest::newRow("noSiblings_pointers") << _(
            "struct A { virtual void virt(); };\n"
            "struct B : A { void virt() {} };\n"
            "struct C1 : B { void virt() {} };\n"
            "struct C2 : B { };\n"
            "struct D : C2 { void virt() {} };\n"
            "\n"
            "void f(C2 *o) { o->$@virt(); }\n")
        << (OverrideItemList()
            << OverrideItem(QLatin1String("B::virt"), 2)
            << OverrideItem(QLatin1String("D::virt"), 5));

    /// Variation of noSiblings_references
    QTest::newRow("noSiblings_noBaseExpression") << _(
            "struct A { virtual void virt() {} };\n"
            "struct B : A { void virt() {} };\n"
            "struct C1 : B { void virt() {} };\n"
            "struct C2 : B { void g() { $@virt(); } };\n"
            "struct D : C2 { void virt() {} };\n")
        << (OverrideItemList()
            << OverrideItem(QLatin1String("B::virt"), 2)
            << OverrideItem(QLatin1String("D::virt"), 5));

    /// Check: Trigger on a.virt() if a is of type &A.
    QTest::newRow("onDotMemberAccessOfReferenceTypes") << _(
            "struct A { virtual void virt() = 0; };\n"
            "void A::virt() {}\n"
            "\n"
            "void client(A &o) { o.$@virt(); }\n")
        << (OverrideItemList()
            << OverrideItem(QLatin1String("A::virt = 0"), 2));

    /// Check: Do not trigger on a.virt() if a is of type A.
    QTest::newRow("notOnDotMemberAccessOfNonReferenceType") << _(
            "struct A { virtual void virt(); };\n"
            "void A::$virt() {}\n"
            "\n"
            "void client(A o) { o.@virt(); }\n")
        << OverrideItemList();

    /// Check: Do not trigger on qualified function calls.
    QTest::newRow("notOnQualified") << _(
            "struct A { virtual void virt(); };\n"
            "void A::$virt() {}\n"
            "\n"
            "struct B : public A {\n"
            "    void virt();\n"
            "    void g() { A::@virt(); }\n"
            "};\n")
        << OverrideItemList();

    /// Check: Do not trigger on member function declaration.
    QTest::newRow("notOnDeclaration") << _(
            "struct A { virtual void virt(); };\n"
            "void A::virt() {}\n"
            "\n"
            "struct B : public A { void virt@(); };\n"
            "void B::$virt() {}\n")
        << OverrideItemList();

    /// Check: Do not trigger on function definition.
    QTest::newRow("notOnDefinition") << _(
            "struct A { virtual void virt(); };\n"
            "void A::virt() {}\n"
            "\n"
            "struct B : public A { void $virt(); };\n"
            "void B::@virt() {}\n")
        << OverrideItemList();

    QTest::newRow("notOnNonPointerNonReference") << _(
            "struct A { virtual void virt(); };\n"
            "void A::virt() {}\n"
            "\n"
            "struct B : public A { void virt(); };\n"
            "void B::$virt() {}\n"
            "\n"
            "void client(B b) { b.@virt(); }\n")
        << OverrideItemList();

    QTest::newRow("differentReturnTypes") << _(
            "struct Base { virtual Base *virt() { return this; } };\n"
            "struct Derived : public Base { Derived *virt() { return this; } };\n"
            "void client(Base *b) { b->$@virt(); }\n")
        << (OverrideItemList()
            << OverrideItem(QLatin1String("Base::virt"), 1)
            << OverrideItem(QLatin1String("Derived::virt"), 2));

    QTest::newRow("QTCREATORBUG-10294_cursorIsAtTheEndOfVirtualFunctionName") << _(
            "struct Base { virtual void virt() {} };\n"
            "struct Derived : Base { void virt() {} };\n"
            "void client(Base *b) { b->virt$@(); }\n")
        << (OverrideItemList()
            << OverrideItem(QLatin1String("Base::virt"), 1)
            << OverrideItem(QLatin1String("Derived::virt"), 2));

    QTest::newRow("static_call") << _(
            "struct Base { virtual void virt() {} };\n"
            "struct Derived : Base { void virt() {} };\n"
            "struct Foo {\n"
            "    static Base *base();\n"
            "};\n"
            "void client() { Foo::base()->$@virt(); }\n")
        << (OverrideItemList()
            << OverrideItem(QLatin1String("Base::virt"), 1)
            << OverrideItem(QLatin1String("Derived::virt"), 2));

    QTest::newRow("double_call") << _(
            "struct Base { virtual void virt() {} };\n"
            "struct Derived : Base { void virt() {} };\n"
            "struct Foo { Base *base(); };\n"
            "Foo *instance();\n"
            "void client() { instance()->base()->$@virt(); }\n")
        << (OverrideItemList()
            << OverrideItem(QLatin1String("Base::virt"), 1)
            << OverrideItem(QLatin1String("Derived::virt"), 2));

    QTest::newRow("casting") << _(
            "struct Base { virtual void virt() {} };\n"
            "struct Derived : Base { void virt() {} };\n"
            "void client() { static_cast<Base *>(0)->$@virt(); }\n")
        << (OverrideItemList()
            << OverrideItem(QLatin1String("Base::virt"), 1)
            << OverrideItem(QLatin1String("Derived::virt"), 2));
}

void CppEditorPlugin::test_FollowSymbolUnderCursor_virtualFunctionCall()
{
    QFETCH(QByteArray, source);
    QFETCH(OverrideItemList, results);

    F2TestCase(F2TestCase::FollowSymbolUnderCursorAction, singleDocument(source), results);
}

/// Check: Base classes can be found although these might be defined in distinct documents.
void CppEditorPlugin::test_FollowSymbolUnderCursor_virtualFunctionCall_multipleDocuments()
{
    QList<TestDocumentPtr> testFiles = QList<TestDocumentPtr>()
            << TestDocument::create("struct A { virtual void virt(int) = 0; };\n",
                                    "a.h")
            << TestDocument::create("#include \"a.h\"\n"
                                    "struct B : A { void virt(int) {} };\n",
                                    "b.h")
            << TestDocument::create("#include \"a.h\"\n"
                                    "void f(A *o) { o->$@virt(42); }\n",
                                    "u.cpp")
            ;

    const OverrideItemList finalResults = OverrideItemList()
            << OverrideItem(QLatin1String("A::virt = 0"), 1)
            << OverrideItem(QLatin1String("B::virt"), 2);

    F2TestCase(F2TestCase::FollowSymbolUnderCursorAction, testFiles, finalResults);
}

} // namespace Internal
} // namespace CppEditor

/*
Potential test cases improving name lookup.

If you fix one, add a test and remove the example from here.

///////////////////////////////////////////////////////////////////////////////////////////////////

// 3.3.1 Declarative regions and scopes, copy-paste (main added)
int j = 24;
int main()
{
    int i = j, j; // First j refers to global j, second j refers to just locally defined j
    j = 42; // Refers to locally defined j
}

///////////////////////////////////////////////////////////////////////////////////////////////////

// 3.3.2 Point of declaration (par. 2), copy-paste (main added)
const int i = 2;
int main()
{
    int i[i]; // Second i refers to global
}

///////////////////////////////////////////////////////////////////////////////////////////////////

// 3.3.2 Point of declaration (par. 9), copy-paste (main added)
typedef unsigned char T;
template<class T
= T // lookup finds the typedef name of unsigned char
, T // lookup finds the template parameter
N = 0> struct A { };

int main() {}

///////////////////////////////////////////////////////////////////////////////////////////////////

// 3.3.9 Template parameter scope (par. 3), copy-paste (main added), part 1
template<class T, T* p, class U = T> class X {}; // second and third T refers to first one
template<class T> void f(T* p = new T);

int main() {}

///////////////////////////////////////////////////////////////////////////////////////////////////

// 3.3.9 Template parameter scope (par. 3), copy-paste (main added), part 2
template<class T> class X : public Array<T> {};
template<class T> class Y : public T {};

int main() {}

///////////////////////////////////////////////////////////////////////////////////////////////////

// 3.3.9 Template parameter scope (par. 4), copy-paste (main added), part 2
typedef int N;
template<N X, typename N, template<N Y> class T> struct A; // N refers to N above

int main() {}

///////////////////////////////////////////////////////////////////////////////////////////////////

// 3.3.10 Name hiding (par 1.), from text, example 2a

// www.stroustrup.com/bs_faq2.html#overloadderived
// "In C++, there is no overloading across scopes - derived class scopes are not
// an exception to this general rule. (See D&E or TC++PL3 for details)."
struct B {
    int f(int) {}
};

struct D : public B {
    double f(double) {} // hides B::f
};

int main()
{
    D* pd = new D;
    pd->f(2); // refers to D::f
    pd->f(2.3); // refers to D::f
}

///////////////////////////////////////////////////////////////////////////////////////////////////

// 3.3.10 Name hiding (par 2.), from text
int C; // hides following type C, order is not important
struct C {};

int main()
{
    ++C;
}

*/
