// 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 "../cplusplus_global.h"

#include <cplusplus/CppDocument.h>
#include <cplusplus/pp.h>

#include <cppeditor/cppchecksymbols.h>
#include <cppeditor/cppsemanticinfo.h>
#include <cppeditor/cpptoolstestcase.h>

#include <texteditor/semantichighlighter.h>

#include <utils/filepath.h>

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

/*!
    Tests CheckSymbols, the "data provider" of the semantic highlighter.
 */

// When adding tests, you may want to set this enum
// in order to print out all found uses.
enum { enableListing = 0 };

using namespace CPlusPlus;
using namespace CppEditor;
using namespace Utils;

typedef QByteArray _;
typedef CheckSymbols::Result Use;
typedef CheckSymbols::Kind UseKind;
typedef SemanticHighlighter Highlighting;
typedef QList<Use> UseList;
Q_DECLARE_METATYPE(UseList)

#define CASE_STR(val) case Highlighting::val: return "Highlighting::" # val
static QString useKindToString(UseKind useKind)
{
    switch (useKind) {
    CASE_STR(Unknown);
    CASE_STR(TypeUse);
    CASE_STR(NamespaceUse);
    CASE_STR(LocalUse);
    CASE_STR(FieldUse);
    CASE_STR(EnumerationUse);
    CASE_STR(VirtualMethodUse);
    CASE_STR(VirtualFunctionDeclarationUse);
    CASE_STR(LabelUse);
    CASE_STR(MacroUse);
    CASE_STR(FunctionUse);
    CASE_STR(FunctionDeclarationUse);
    CASE_STR(PseudoKeywordUse);
    CASE_STR(StaticFieldUse);
    CASE_STR(StaticMethodUse);
    CASE_STR(StaticMethodDeclarationUse);
    default:
        QTest::qFail("Unknown UseKind", __FILE__, __LINE__);
        return QLatin1String("Unknown UseKind");
    }
}
#undef CASE_STR

// The following two functions are "enhancements" for QCOMPARE().
QT_BEGIN_NAMESPACE
namespace QTest {

template<>
char *toString(const Use &use)
{
    QByteArray ba = "Use(";
    ba += QByteArray::number(use.line);
    ba += ", " + QByteArray::number(use.column);
    ba += ", " + QByteArray::number(use.length);
    ba += ", " + useKindToString(static_cast<UseKind>(use.kind)).toLatin1();
    ba += ")";
    return qstrdup(ba.data());
}

} // namespace QTest
QT_END_NAMESPACE

namespace {

class BaseTestCase
{
public:
    BaseTestCase(const QByteArray &source, const UseList &expectedUsesMacros = UseList())
    {
        // Write source to temporary file
        const QString filePath = QDir::tempPath() + QLatin1String("/file.h");
        Tests::TestCase::writeFile(FilePath::fromString(filePath), source);

        // Process source
        const Document::Ptr document = createDocument(filePath, source);
        Snapshot snapshot;
        snapshot.insert(document);

        // Collect symbols
        future = runCheckSymbols(document, snapshot, expectedUsesMacros);
    }

    static CheckSymbols::Future runCheckSymbols(const Document::Ptr &document,
                                                const Snapshot &snapshot,
                                                const UseList &expectedUsesMacros = UseList())
    {
        LookupContext context(document, snapshot);
        CheckSymbols::Future future = CheckSymbols::go(document, {}, context, expectedUsesMacros);
        future.waitForFinished();
        return future;
    }

    static Document::Ptr createDocument(const QString &filePath, const QByteArray &source)
    {
        CPlusPlus::Environment env;
        Preprocessor preprocess(0, &env);
        preprocess.setKeepComments(true);
        const QByteArray preprocessedSource = preprocess.run(filePath, source);

        Document::Ptr document = Document::create(Utils::FilePath::fromString(filePath));
        document->setUtf8Source(preprocessedSource);
        if (!document->parse())
            return Document::Ptr();
        document->check();
        if (!document->diagnosticMessages().isEmpty())
            return Document::Ptr();

        return document;
    }

    Use findUse(int line, int column)
    {
        const int resultCount = future.resultCount();
        for (int i = resultCount - 1; i >= 0; --i) {
            Use result = future.resultAt(i);
            if (result.line > line)
                continue;
            if (result.line < line || result.column < column)
                break;
            if (result.column == column)
                return result;
        }
        return {};
    }

    CheckSymbols::Future future;
};

class TestCase : public BaseTestCase
{
public:
    TestCase(const QByteArray &source,
             const UseList &expectedUsesAll,
             const UseList &expectedUsesMacros = UseList())
        : BaseTestCase(source, expectedUsesMacros)
    {
        const int resultCount = future.resultCount();
        UseList actualUses;
        QByteArray expectedInput;
        if (enableListing)
            expectedInput = _("\n") + _(8, ' ') + "<< (UseList()\n";
        for (int i = 0; i < resultCount; ++i) {
            const Use use = future.resultAt(i);
            if (enableListing)
                expectedInput += _(12, ' ') + "<< " + _(QTest::toString(use)) + "\n";
            actualUses.append(use);
        }

        if (enableListing) {
            expectedInput.chop(1);
            expectedInput += ')';
            qDebug() << expectedInput;
        }
        // Checks
        QVERIFY(resultCount > 0);
        QCOMPARE(resultCount, expectedUsesAll.count());

        for (int i = 0; i < resultCount; ++i) {
            const Use actualUse = actualUses.at(i);
            const Use expectedUse = expectedUsesAll.at(i);
            QVERIFY(actualUse.isValid());
            QVERIFY(expectedUse.isValid());
            QCOMPARE(actualUse, expectedUse);
        }
    }
};

} // anonymous namespace

class tst_CheckSymbols: public QObject
{
    Q_OBJECT

private slots:

    void test_checksymbols();
    void test_checksymbols_data();

    void test_checksymbols_macroUses();
    void test_checksymbols_macroUses_data();

    void test_checksymbols_infiniteLoop_data();
    void test_checksymbols_infiniteLoop();

    void test_checkForValidSymbolFileId();

    void test_parentOfBlock();
    void test_infiniteLoop();

    void findField();
    void findField_data();
};

void tst_CheckSymbols::test_checksymbols()
{
    QFETCH(QByteArray, source);
    QFETCH(UseList, expectedUsesAll);

    TestCase(source, expectedUsesAll);
}

void tst_CheckSymbols::test_checksymbols_data()
{
    QTest::addColumn<QByteArray>("source");
    QTest::addColumn<UseList>("expectedUsesAll");

    QTest::newRow("TypeUse")
        << _("namespace N {}\n"
             "using namespace N;\n")
        << (UseList()
            << Use(1, 11, 1, Highlighting::NamespaceUse)
            << Use(2, 17, 1, Highlighting::NamespaceUse));

    QTest::newRow("LocalUse")
        << _("int f()\n"
             "{\n"
             "   int i;\n"
             "}\n")
        << (UseList()
            << Use(1, 5, 1, Highlighting::FunctionDeclarationUse)
            << Use(3, 8, 1, Highlighting::LocalUse));

    QTest::newRow("FieldUse")
        << _("struct F {\n"          // 1
             "    int i;\n"
             "    F() { i = 0; }\n"
             "};\n"
             "int f()\n"             // 5
             "{\n"
             "    F s;\n"
             "    s.i = 2;\n"
             "}\n")
        << (UseList()
            << Use(1, 8, 1, Highlighting::TypeUse)
            << Use(2, 9, 1, Highlighting::FieldUse)
            << Use(3, 5, 1, Highlighting::TypeUse)
            << Use(3, 11, 1, Highlighting::FieldUse)
            << Use(5, 5, 1, Highlighting::FunctionDeclarationUse)
            << Use(7, 5, 1, Highlighting::TypeUse)
            << Use(7, 7, 1, Highlighting::LocalUse)
            << Use(8, 5, 1, Highlighting::LocalUse)
            << Use(8, 7, 1, Highlighting::FieldUse));

    QTest::newRow("EnumerationUse")
        << _("enum E { Red, Green, Blue };\n"
             "E e = Red;\n")
        << (UseList()
            << Use(1, 6, 1, Highlighting::TypeUse)
            << Use(1, 10, 3, Highlighting::EnumerationUse)
            << Use(1, 15, 5, Highlighting::EnumerationUse)
            << Use(1, 22, 4, Highlighting::EnumerationUse)
            << Use(2, 1, 1, Highlighting::TypeUse)
            << Use(2, 7, 3, Highlighting::EnumerationUse));

    QTest::newRow("VirtualFunction")
        << _("class B {\n"                   // 1
             "    virtual bool isThere();\n" // 2
             "    bool nonVirtual() {\n"     // 3
             "        return isThere();\n"   // 4
             "    }\n"                       // 5
             "};\n"                          // 6
             "class D: public B {\n"         // 7
             "    bool isThere();\n"         // 8
             "};\n")
        << (UseList()
            << Use(1, 7, 1, Highlighting::TypeUse)              // B
            << Use(2, 18, 7, Highlighting::VirtualFunctionDeclarationUse)    // isThere
            << Use(3, 10, 10, Highlighting::FunctionDeclarationUse)    // nonVirtual
            << Use(4, 16, 7, Highlighting::VirtualMethodUse)    // isThere call
            << Use(7, 7, 1, Highlighting::TypeUse)              // D
            << Use(7, 17, 1, Highlighting::TypeUse)             // B
            << Use(8, 10, 7, Highlighting::VirtualFunctionDeclarationUse));  // isThere

    QTest::newRow("LabelUse")
        << _("int f()\n"
             "{\n"
             "   goto g;\n"
             "   g: return 1;\n"
             "}\n")
        << (UseList()
            << Use(1, 5, 1, Highlighting::FunctionDeclarationUse)
            << Use(3, 9, 1, Highlighting::LabelUse)
            << Use(4, 4, 1, Highlighting::LabelUse));

    QTest::newRow("FunctionUse")
        << _("int f();\n"
             "int g() { f(); }\n")
        << (UseList()
            << Use(1, 5, 1, Highlighting::FunctionDeclarationUse)
            << Use(2, 5, 1, Highlighting::FunctionDeclarationUse)
            << Use(2, 11, 1, Highlighting::FunctionUse));

    QTest::newRow("PseudoKeywordUse")
        << _("class D : public B {\n"
             "   virtual void f() override {}\n"
             "   virtual void f() final {}\n"
             "};\n")
        << (UseList()
            << Use(1, 7, 1, Highlighting::TypeUse)
            << Use(2, 17, 1, Highlighting::VirtualFunctionDeclarationUse)
            << Use(2, 21, 8, Highlighting::PseudoKeywordUse)
            << Use(3, 17, 1, Highlighting::VirtualFunctionDeclarationUse)
            << Use(3, 21, 5, Highlighting::PseudoKeywordUse));

    QTest::newRow("StaticUse")
        << _("struct Outer\n"
             "{\n"
             "    static int Foo;\n"
             "    struct Inner\n"
             "    {\n"
             "        Outer *outer;\n"
             "        void foo();\n"
             "    };\n"
             "};\n"
             "\n"
             "int Outer::Foo = 42;\n"
             "\n"
             "void Outer::Inner::foo()\n"
             "{\n"
             "    Foo  = 7;\n"
             "    Outer::Foo = 7;\n"
             "    outer->Foo = 7;\n"
             "}\n")
        << (UseList()
            << Use(1, 8, 5, Highlighting::TypeUse)
            << Use(3, 16, 3, Highlighting::StaticFieldUse)
            << Use(4, 12, 5, Highlighting::TypeUse)
            << Use(6, 9, 5, Highlighting::TypeUse)
            << Use(6, 16, 5, Highlighting::FieldUse)
            << Use(7, 14, 3, Highlighting::FunctionDeclarationUse)
            << Use(11, 5, 5, Highlighting::TypeUse)
            << Use(11, 12, 3, Highlighting::FieldUse) // FIXME: Should be StaticField
            << Use(13, 6, 5, Highlighting::TypeUse)
            << Use(13, 13, 5, Highlighting::TypeUse)
            << Use(13, 20, 3, Highlighting::FunctionDeclarationUse)
            << Use(15, 5, 3, Highlighting::StaticFieldUse)
            << Use(16, 5, 5, Highlighting::TypeUse)
            << Use(16, 12, 3, Highlighting::FieldUse) // FIXME: Should be StaticField
            << Use(17, 5, 5, Highlighting::FieldUse)
            << Use(17, 12, 3, Highlighting::StaticFieldUse));

    QTest::newRow("VariableHasTheSameNameAsEnumUse")
        << _("struct Foo\n"
             "{\n"
             "    enum E { bar, baz };\n"
             "};\n"
             "\n"
             "struct Boo\n"
             "{\n"
             "    int foo;\n"
             "    int bar;\n"
             "    int baz;\n"
             "};\n")
        << (UseList()
            << Use(1, 8, 3, Highlighting::TypeUse)
            << Use(3, 10, 1, Highlighting::TypeUse)
            << Use(3, 14, 3, Highlighting::EnumerationUse)
            << Use(3, 19, 3, Highlighting::EnumerationUse)
            << Use(6, 8, 3, Highlighting::TypeUse)
            << Use(8, 9, 3, Highlighting::FieldUse)
            << Use(9, 9, 3, Highlighting::FieldUse)
            << Use(10, 9, 3, Highlighting::FieldUse));

    QTest::newRow("NestedClassOfEnclosingTemplateUse")
        << _("struct Foo { int bar; };\n"
             "\n"
             "template<typename T>\n"
             "struct Outer\n"
             "{\n"
             "    struct Nested { T nt; } nested;\n"
             "};\n"
             "\n"
             "void fun()\n"
             "{\n"
             "    Outer<Foo> list;\n"
             "    list.nested.nt.bar;\n"
             "}\n")
        << (UseList()
            << Use(1, 8, 3, Highlighting::TypeUse)
            << Use(1, 18, 3, Highlighting::FieldUse)
            << Use(3, 19, 1, Highlighting::TypeUse)
            << Use(4, 8, 5, Highlighting::TypeUse)
            << Use(6, 12, 6, Highlighting::TypeUse)
            << Use(6, 21, 1, Highlighting::TypeUse)
            << Use(6, 23, 2, Highlighting::FieldUse)
            << Use(6, 29, 6, Highlighting::FieldUse)
            << Use(9, 6, 3, Highlighting::FunctionDeclarationUse)
            << Use(11, 5, 5, Highlighting::TypeUse)
            << Use(11, 11, 3, Highlighting::TypeUse)
            << Use(11, 16, 4, Highlighting::LocalUse)
            << Use(12, 5, 4, Highlighting::LocalUse)
            << Use(12, 10, 6, Highlighting::FieldUse)
            << Use(12, 17, 2, Highlighting::FieldUse)
            << Use(12, 20, 3, Highlighting::FieldUse));

    QTest::newRow("8902_staticFunctionHighlightingAsMember_localVariable")
        << _("struct Foo\n"
             "{\n"
             "    static int foo();\n"
             "};\n"
             "\n"
             "void bar()\n"
             "{\n"
             "    int foo = Foo::foo();\n"
             "}\n")
        << (UseList()
            << Use(1, 8, 3, Highlighting::TypeUse)
            << Use(3, 16, 3, Highlighting::StaticMethodDeclarationUse)
            << Use(6, 6, 3, Highlighting::FunctionDeclarationUse)
            << Use(8, 9, 3, Highlighting::LocalUse)
            << Use(8, 15, 3, Highlighting::TypeUse)
            << Use(8, 20, 3, Highlighting::StaticMethodUse));

    QTest::newRow("8902_staticFunctionHighlightingAsMember_functionArgument")
        << _("struct Foo\n"
             "{\n"
             "    static int foo();\n"
             "};\n"
             "\n"
             "void bar(int foo)\n"
             "{\n"
             "    Foo::foo();\n"
             "}\n")
        << (UseList()
            << Use(1, 8, 3, Highlighting::TypeUse)
            << Use(3, 16, 3, Highlighting::StaticMethodDeclarationUse)
            << Use(6, 6, 3, Highlighting::FunctionDeclarationUse)
            << Use(6, 14, 3, Highlighting::LocalUse)
            << Use(8, 5, 3, Highlighting::TypeUse)
            << Use(8, 10, 3, Highlighting::StaticMethodUse));

    QTest::newRow("8902_staticFunctionHighlightingAsMember_templateParameter")
        << _("struct Foo\n"
             "{\n"
             "    static int foo();\n"
             "};\n"
             "\n"
             "template <class foo>\n"
             "void bar()\n"
             "{\n"
             "    Foo::foo();\n"
             "}\n")
        << (UseList()
            << Use(1, 8, 3, Highlighting::TypeUse)
            << Use(3, 16, 3, Highlighting::StaticMethodDeclarationUse)
            << Use(6, 17, 3, Highlighting::TypeUse)
            << Use(7, 6, 3, Highlighting::FunctionDeclarationUse)
            << Use(9, 5, 3, Highlighting::TypeUse)
            << Use(9, 10, 3, Highlighting::StaticMethodUse));

    QTest::newRow("staticFunctionHighlightingAsMember_struct")
        << _("struct Foo\n"
             "{\n"
             "    static int foo();\n"
             "};\n"
             "\n"
             "struct foo {};\n"
             "void bar()\n"
             "{\n"
             "    Foo::foo();\n"
             "}\n")
        << (UseList()
            << Use(1, 8, 3, Highlighting::TypeUse)
            << Use(3, 16, 3, Highlighting::StaticMethodDeclarationUse)
            << Use(6, 8, 3, Highlighting::TypeUse)
            << Use(7, 6, 3, Highlighting::FunctionDeclarationUse)
            << Use(9, 5, 3, Highlighting::TypeUse)
            << Use(9, 10, 3, Highlighting::StaticMethodUse));

    QTest::newRow("QTCREATORBUG8890_danglingPointer")
        << _("template<class T> class QList {\n"
             "    public:\n"
             "        T operator[](int);\n"
             "};\n"
             "\n"
             "template<class T> class QPointer {\n"
             "    public:\n"
             "        T& operator->();\n"
             "};\n"
             "\n"
             "class Foo {\n"
             "    void foo() {}\n"
             "};\n"
             "\n"
             "void f()\n"
             "{\n"
             "    QList<QPointer<Foo> > list;\n"
             "    list[0]->foo();\n"
             "    list[0]->foo(); // Crashed because of this 'extra' line.\n"
             "}\n")
        << (UseList()
            << Use(1, 16, 1, Highlighting::TypeUse)
            << Use(1, 25, 5, Highlighting::TypeUse)
            << Use(3, 9, 1, Highlighting::TypeUse)
            << Use(3, 11, 8, Highlighting::FunctionDeclarationUse)
            << Use(6, 16, 1, Highlighting::TypeUse)
            << Use(6, 25, 8, Highlighting::TypeUse)
            << Use(8, 9, 1, Highlighting::TypeUse)
            << Use(8, 12, 8, Highlighting::FunctionDeclarationUse)
            << Use(11, 7, 3, Highlighting::TypeUse)
            << Use(12, 10, 3, Highlighting::FunctionDeclarationUse)
            << Use(15, 6, 1, Highlighting::FunctionDeclarationUse)
            << Use(17, 5, 5, Highlighting::TypeUse)
            << Use(17, 11, 8, Highlighting::TypeUse)
            << Use(17, 20, 3, Highlighting::TypeUse)
            << Use(17, 27, 4, Highlighting::LocalUse)
            << Use(18, 5, 4, Highlighting::LocalUse)
            << Use(18, 14, 3, Highlighting::FunctionUse)
            << Use(19, 5, 4, Highlighting::LocalUse)
            << Use(19, 14, 3, Highlighting::FunctionUse));

    // TODO: This is a good candidate for a performance test.
    QByteArray excessive =
             "template <class T>\n"
             "class Singleton\n"
             "{\n"
             "public:\n"
             "    static T& instance() {}\n"
             "};\n"
             "\n"
             "void bar() {}\n"
             "\n"
             "void foo()\n"
             "{\n"
            ;
    for (int i = 0; i < 250; ++i)
        excessive += "    Singleton<INIManager>::instance().bar();\n";
    excessive += "}\n";
    UseList excessiveUses;
    excessiveUses
            << Use(1, 17, 1, Highlighting::TypeUse)
            << Use(2, 7, 9, Highlighting::TypeUse)
            << Use(5, 12, 1, Highlighting::TypeUse)
            << Use(5, 15, 8, Highlighting::StaticMethodDeclarationUse)
            << Use(8, 6, 3, Highlighting::FunctionDeclarationUse)
            << Use(10, 6, 3, Highlighting::FunctionDeclarationUse);
    for (int i = 0; i < 250; ++i) {
        excessiveUses
                << Use(12 + i, 5, 9, Highlighting::TypeUse)
                << Use(12 + i, 28, 8, Highlighting::StaticMethodUse);
    }
    QTest::newRow("QTCREATORBUG8974_danglingPointer")
        << excessive
        << excessiveUses;

    QTest::newRow("operatorAsteriskOfNestedClassOfTemplateClass_QTCREATORBUG9006")
        << _("struct Foo { int foo; };\n"
             "\n"
             "template<class T>\n"
             "struct Outer\n"
             "{\n"
             "  struct Nested\n"
             "  {\n"
             "    const T &operator*() { return t; }\n"
             "    T t;\n"
             "  };\n"
             "};\n"
             "\n"
             "void bug()\n"
             "{\n"
             "  Outer<Foo>::Nested nested;\n"
             "  (*nested).foo;\n"
             "}\n")
        << (UseList()
            << Use(1, 8, 3, Highlighting::TypeUse)
            << Use(1, 18, 3, Highlighting::FieldUse)
            << Use(3, 16, 1, Highlighting::TypeUse)
            << Use(4, 8, 5, Highlighting::TypeUse)
            << Use(6, 10, 6, Highlighting::TypeUse)
            << Use(8, 11, 1, Highlighting::TypeUse)
            << Use(8, 14, 8, Highlighting::FunctionDeclarationUse)
            << Use(8, 35, 1, Highlighting::FieldUse)
            << Use(9, 5, 1, Highlighting::TypeUse)
            << Use(9, 7, 1, Highlighting::FieldUse)
            << Use(13, 6, 3, Highlighting::FunctionDeclarationUse)
            << Use(15, 3, 5, Highlighting::TypeUse)
            << Use(15, 9, 3, Highlighting::TypeUse)
            << Use(15, 15, 6, Highlighting::TypeUse)
            << Use(15, 22, 6, Highlighting::LocalUse)
            << Use(16, 5, 6, Highlighting::LocalUse)
            << Use(16, 13, 3, Highlighting::FieldUse));

    QTest::newRow("templated_functions")
        << _("struct D {};\n"                      // line 1
             "struct A {\n"                        // line 2
             "    template<typename T> int B();\n" // line 3
             "    void C() {\n"                    // line 4
             "        B<D>();\n"                   // line 5
             "        this->B<D>();\n"             // line 6
             "    }\n"                             // line 7
             "};\n")                               // line 8
        << (UseList()
            << Use(1, 8, 1, Highlighting::TypeUse)
            << Use(2, 8, 1, Highlighting::TypeUse)
            << Use(3, 23, 1, Highlighting::TypeUse)
            << Use(3, 30, 1, Highlighting::FunctionDeclarationUse)
            << Use(4, 10, 1, Highlighting::FunctionDeclarationUse)
            << Use(5, 9, 1, Highlighting::FunctionUse)
            << Use(5, 11, 1, Highlighting::TypeUse)
            << Use(6, 15, 1, Highlighting::FunctionUse)
            << Use(6, 17, 1, Highlighting::TypeUse));

    QTest::newRow("AnonymousClass")
        << _("struct\n"
             "{\n"
             "  int foo;\n"
             "} Foo;\n"
             "\n"
             "void fun()\n"
             "{\n"
             "  foo = 3;\n"
             "}\n")
        << (UseList()
            << Use(3, 7, 3, Highlighting::FieldUse)
            << Use(6, 6, 3, Highlighting::FunctionDeclarationUse));

    QTest::newRow("QTCREATORBUG9098")
        << _("template <typename T>\n"
             "class B\n"
             "{\n"
             "public:\n"
             "    C<T> c;\n"
             "};\n"
             "template <typename T>\n"
             "class A\n"
             "{\n"
             "public:\n"
             "    B<T> b;\n"
             "    void fun()\n"
             "    {\n"
             "        b.c;\n"
             "    }\n"
             "};\n")
        << (UseList()
            << Use(1, 20, 1, Highlighting::TypeUse)
            << Use(2, 7, 1, Highlighting::TypeUse)
            << Use(5, 7, 1, Highlighting::TypeUse)
            << Use(5, 10, 1, Highlighting::FieldUse)
            << Use(7, 20, 1, Highlighting::TypeUse)
            << Use(8, 7, 1, Highlighting::TypeUse)
            << Use(11, 5, 1, Highlighting::TypeUse)
            << Use(11, 7, 1, Highlighting::TypeUse)
            << Use(11, 10, 1, Highlighting::FieldUse)
            << Use(12, 10, 3, Highlighting::FunctionDeclarationUse)
            << Use(14, 9, 1, Highlighting::FieldUse)
            << Use(14, 11, 1, Highlighting::FieldUse));

    QTest::newRow("AnonymousClass_insideNamespace")
        << _("struct { int foo1; } Foo1;\n"
             "void bar1()\n"
             "{\n"
             "  Foo1.foo1 = 42;\n"
             "}\n"
             "namespace Ns1 {\n"
             "  struct { int foo2; } Foo2;\n"
             "  void bar2()\n"
             "  {\n"
             "    Foo2.foo2 = 42;\n"
             "  }\n"
             "}\n"
             "namespace Ns2 {\n"
             "  struct {\n"
             "    struct { struct { int foo3; }; };\n"
             "    void func() { foo3 = 42; }\n"
             "  } Foo3;\n"
             "  void bar3()\n"
             "  {\n"
             "    Foo3.foo3 = 42;\n"
             "  }\n"
             "}\n")
        << (UseList()
            << Use(1, 14, 4, Highlighting::FieldUse)
            << Use(2, 6, 4, Highlighting::FunctionDeclarationUse)
            << Use(4, 8, 4, Highlighting::FieldUse)
            << Use(6, 11, 3, Highlighting::NamespaceUse)
            << Use(7, 16, 4, Highlighting::FieldUse)
            << Use(8, 8, 4, Highlighting::FunctionDeclarationUse)
            << Use(10, 10, 4, Highlighting::FieldUse)
            << Use(13, 11, 3, Highlighting::NamespaceUse)
            << Use(15, 27, 4, Highlighting::FieldUse)
            << Use(16, 10, 4, Highlighting::FunctionDeclarationUse)
            << Use(16, 19, 4, Highlighting::FieldUse)
            << Use(18, 8, 4, Highlighting::FunctionDeclarationUse)
            << Use(20, 10, 4, Highlighting::FieldUse));

    QTest::newRow("AnonymousClass_insideFunction")
        << _("int foo()\n"
              "{\n"
              "    union\n"
              "    {\n"
              "        int foo1;\n"
              "        int foo2;\n"
              "    };\n"
              "}\n")
        << (UseList()
            << Use(1, 5, 3, Highlighting::FunctionDeclarationUse)
            << Use(5, 13, 4, Highlighting::FieldUse)
            << Use(6, 13, 4, Highlighting::FieldUse));

    QTest::newRow("AnonymousClass_QTCREATORBUG8963")
        << _("typedef enum {\n"
              "    FIRST\n"
              "} isNotInt;\n"
              "typedef struct {\n"
              "    int isint;\n"
              "    int isNotInt;\n"
              "} Struct;\n"
              "void foo()\n"
              "{\n"
              "    Struct s;\n"
              "    s.isint;\n"
              "    s.isNotInt;\n"
              "    FIRST;\n"
              "}\n")
        << (UseList()
            << Use(2, 5, 5, Highlighting::EnumerationUse)
            << Use(3, 3, 8, Highlighting::TypeUse)
            << Use(5, 9, 5, Highlighting::FieldUse)
            << Use(6, 9, 8, Highlighting::FieldUse)
            << Use(7, 3, 6, Highlighting::TypeUse)
            << Use(8, 6, 3, Highlighting::FunctionDeclarationUse)
            << Use(10, 5, 6, Highlighting::TypeUse)
            << Use(10, 12, 1, Highlighting::LocalUse)
            << Use(11, 5, 1, Highlighting::LocalUse)
            << Use(11, 7, 5, Highlighting::FieldUse)
            << Use(12, 5, 1, Highlighting::LocalUse)
            << Use(12, 7, 8, Highlighting::FieldUse)
            << Use(13, 5, 5, Highlighting::EnumerationUse));

    QTest::newRow("class_declaration_with_object_name_nested_in_function")
        << _("int foo()\n"
              "{\n"
              "    struct Nested\n"
              "    {\n"
              "        int i;\n"
              "    } n;\n"
              "    n.i = 42;\n"
              "}\n")
        << (UseList()
            << Use(1, 5, 3, Highlighting::FunctionDeclarationUse)
            << Use(3, 12, 6, Highlighting::TypeUse)
            << Use(5, 13, 1, Highlighting::FieldUse)
            << Use(6, 7, 1, Highlighting::LocalUse)
            << Use(7, 5, 1, Highlighting::LocalUse)
            << Use(7, 7, 1, Highlighting::FieldUse));

    QTest::newRow("highlightingTypeWhenUsingNamespaceClass_QTCREATORBUG7903_globalNamespace")
        << _("namespace NS {\n"
             "class Foo {};\n"
             "}\n"
             "using NS::Foo;\n"
             "void fun()\n"
             "{\n"
             "    Foo foo;\n"
             "}\n")
        << (UseList()
            << Use(1, 11, 2, Highlighting::NamespaceUse)
            << Use(2, 7, 3, Highlighting::TypeUse)
            << Use(4, 7, 2, Highlighting::NamespaceUse)
            << Use(4, 11, 3, Highlighting::TypeUse)
            << Use(5, 6, 3, Highlighting::FunctionDeclarationUse)
            << Use(7, 5, 3, Highlighting::TypeUse)
            << Use(7, 9, 3, Highlighting::LocalUse));

    QTest::newRow("highlightingTypeWhenUsingNamespaceClass_QTCREATORBUG7903_namespace")
        << _("namespace NS {\n"
              "class Foo {};\n"
              "}\n"
              "namespace NS1 {\n"
              "using NS::Foo;\n"
              "void fun()\n"
              "{\n"
              "    Foo foo;\n"
              "}\n"
              "}\n")
        << (UseList()
            << Use(1, 11, 2, Highlighting::NamespaceUse)
            << Use(2, 7, 3, Highlighting::TypeUse)
            << Use(4, 11, 3, Highlighting::NamespaceUse)
            << Use(5, 7, 2, Highlighting::NamespaceUse)
            << Use(5, 11, 3, Highlighting::TypeUse)
            << Use(6, 6, 3, Highlighting::FunctionDeclarationUse)
            << Use(8, 5, 3, Highlighting::TypeUse)
            << Use(8, 9, 3, Highlighting::LocalUse));

    QTest::newRow("highlightingTypeWhenUsingNamespaceClass_QTCREATORBUG7903_insideFunction")
        << _("namespace NS {\n"
             "class Foo {};\n"
             "}\n"
             "void fun()\n"
             "{\n"
             "    using NS::Foo;\n"
             "    Foo foo;\n"
             "}\n")
        << (UseList()
            << Use(1, 11, 2, Highlighting::NamespaceUse)
            << Use(2, 7, 3, Highlighting::TypeUse)
            << Use(4, 6, 3, Highlighting::FunctionDeclarationUse)
            << Use(6, 11, 2, Highlighting::NamespaceUse)
            << Use(6, 15, 3, Highlighting::TypeUse)
            << Use(7, 5, 3, Highlighting::TypeUse)
            << Use(7, 9, 3, Highlighting::LocalUse));

    QTest::newRow("crashWhenUsingNamespaceClass_QTCREATORBUG9323_globalNamespace")
        << _("namespace NS {\n"
             "class Foo {};\n"
             "}\n"
             "using ::;\n"
             "void fun()\n"
             "{\n"
             "    Foo foo;\n"
             "}\n")
        << (UseList()
            << Use(1, 11, 2, Highlighting::NamespaceUse)
            << Use(2, 7, 3, Highlighting::TypeUse)
            << Use(5, 6, 3, Highlighting::FunctionDeclarationUse)
            << Use(7, 9, 3, Highlighting::LocalUse));

    QTest::newRow("crashWhenUsingNamespaceClass_QTCREATORBUG9323_namespace")
        << _("namespace NS {\n"
             "class Foo {};\n"
             "}\n"
             "namespace NS1 {\n"
             "using ::;\n"
             "void fun()\n"
             "{\n"
             "    Foo foo;\n"
             "}\n"
             "}\n")
        << (UseList()
            << Use(1, 11, 2, Highlighting::NamespaceUse)
            << Use(2, 7, 3, Highlighting::TypeUse)
            << Use(4, 11, 3, Highlighting::NamespaceUse)
            << Use(6, 6, 3, Highlighting::FunctionDeclarationUse)
            << Use(8, 9, 3, Highlighting::LocalUse));

    QTest::newRow("crashWhenUsingNamespaceClass_QTCREATORBUG9323_insideFunction")
        << _("namespace NS {\n"
             "class Foo {};\n"
             "}\n"
             "void fun()\n"
             "{\n"
             "    using ::;\n"
             "    Foo foo;\n"
             "}\n")
        << (UseList()
            << Use(1, 11, 2, Highlighting::NamespaceUse)
            << Use(2, 7, 3, Highlighting::TypeUse)
            << Use(4, 6, 3, Highlighting::FunctionDeclarationUse)
            << Use(7, 9, 3, Highlighting::LocalUse));

    QTest::newRow("alias_decl_QTCREATORBUG9386")
        << _("using wobble = int;\n"
             "wobble cobble = 1;\n")
        << (UseList()
            << Use(1, 7, 6, Highlighting::TypeUse)
            << Use(2, 1, 6, Highlighting::TypeUse));

    QTest::newRow("enum_inside_block_inside_function_QTCREATORBUG5456")
        << _("void foo()\n"
             "{\n"
             "   {\n"
             "       enum E { e1, e2, e3 };\n"
             "       E e = e1;\n"
             "   }\n"
             "}\n")
        << (UseList()
            << Use(1, 6, 3, Highlighting::FunctionDeclarationUse)
            << Use(4, 13, 1, Highlighting::TypeUse)
            << Use(4, 17, 2, Highlighting::EnumerationUse)
            << Use(4, 21, 2, Highlighting::EnumerationUse)
            << Use(4, 25, 2, Highlighting::EnumerationUse)
            << Use(5, 8, 1, Highlighting::TypeUse)
            << Use(5, 10, 1, Highlighting::LocalUse)
            << Use(5, 14, 2, Highlighting::EnumerationUse));

    QTest::newRow("enum_inside_function_QTCREATORBUG5456")
        << _("void foo()\n"
             "{\n"
             "   enum E { e1, e2, e3 };\n"
             "   E e = e1;\n"
             "}\n")
        << (UseList()
            << Use(1, 6, 3, Highlighting::FunctionDeclarationUse)
            << Use(3, 9, 1, Highlighting::TypeUse)
            << Use(3, 13, 2, Highlighting::EnumerationUse)
            << Use(3, 17, 2, Highlighting::EnumerationUse)
            << Use(3, 21, 2, Highlighting::EnumerationUse)
            << Use(4, 4, 1, Highlighting::TypeUse)
            << Use(4, 6, 1, Highlighting::LocalUse)
            << Use(4, 10, 2, Highlighting::EnumerationUse));

    QTest::newRow("using_inside_different_namespace_QTCREATORBUG7978")
        << _("struct S {};\n"
             "namespace std\n"
             "{\n"
             "    template <typename T> struct shared_ptr{};\n"
             "}\n"
             "namespace NS\n"
             "{\n"
             "    using std::shared_ptr;\n"
             "}\n"
             "void fun()\n"
             "{\n"
             "    NS::shared_ptr<S> p;\n"
             "}\n")
        << (UseList()
            << Use(1, 8, 1, Highlighting::TypeUse)
            << Use(2, 11, 3, Highlighting::NamespaceUse)
            << Use(4, 24, 1, Highlighting::TypeUse)
            << Use(4, 34, 10, Highlighting::TypeUse)
            << Use(6, 11, 2, Highlighting::NamespaceUse)
            << Use(8, 11, 3, Highlighting::NamespaceUse)
            << Use(8, 16, 10, Highlighting::TypeUse)
            << Use(10, 6, 3, Highlighting::FunctionDeclarationUse)
            << Use(12, 5, 2, Highlighting::NamespaceUse)
            << Use(12, 9, 10, Highlighting::TypeUse)
            << Use(12, 20, 1, Highlighting::TypeUse)
            << Use(12, 23, 1, Highlighting::LocalUse));

    QTest::newRow("using_inside_different_block_of_scope_unnamed_namespace_QTCREATORBUG12357")
            << _("namespace \n"
                 "{\n"
                 "    namespace Ns { struct Foo {}; }\n"
                 "    using Ns::Foo;\n"
                 "}\n"
                 "void fun()\n"
                 "{\n"
                 "    Foo foo;\n"
                 "}\n")
            << (QList<Use>()
                << Use(3, 15, 2, Highlighting::NamespaceUse)
                << Use(3, 27, 3, Highlighting::TypeUse)
                << Use(4, 11, 2, Highlighting::NamespaceUse)
                << Use(4, 15, 3, Highlighting::TypeUse)
                << Use(6, 6, 3, Highlighting::FunctionDeclarationUse)
                << Use(8, 5, 3, Highlighting::TypeUse)
                << Use(8, 9, 3, Highlighting::LocalUse)
                );

    QTest::newRow("using_inside_different_block_of_scope_named_namespace_QTCREATORBUG12357")
            << _("namespace NS1\n"
                 "{\n"
                 "    namespace Ns { struct Foo {}; }\n"
                 "    using Ns::Foo;\n"
                 "}\n"
                 "namespace NS1\n"
                 "{\n"
                 "    void fun()\n"
                 "    {\n"
                 "        Foo foo;\n"
                 "    }\n"
                 "}\n"
                 )
            << (QList<Use>()
                << Use(1, 11, 3, Highlighting::NamespaceUse)
                << Use(3, 15, 2, Highlighting::NamespaceUse)
                << Use(3, 27, 3, Highlighting::TypeUse)
                << Use(4, 11, 2, Highlighting::NamespaceUse)
                << Use(4, 15, 3, Highlighting::TypeUse)
                << Use(6, 11, 3, Highlighting::NamespaceUse)
                << Use(8, 10, 3, Highlighting::FunctionDeclarationUse)
                << Use(10, 13, 3, Highlighting::LocalUse)
                );

    QTest::newRow("template_alias")
            << _("template<class T>\n"
                 "using Foo = Bar<T>;\n")
            << (QList<Use>()
                << Use(1, 16, 1, Highlighting::TypeUse)
                << Use(2, 7, 3, Highlighting::TypeUse)
                << Use(2, 17, 1, Highlighting::TypeUse)
                );

    QTest::newRow("using_inside_different_namespace_QTCREATORBUG7978")
        << _("class My" TEST_UNICODE_IDENTIFIER "Type { int " TEST_UNICODE_IDENTIFIER "Member; };\n"
             "void f(My" TEST_UNICODE_IDENTIFIER "Type var" TEST_UNICODE_IDENTIFIER ")\n"
             "{ var" TEST_UNICODE_IDENTIFIER "." TEST_UNICODE_IDENTIFIER "Member = 0; }\n")
        << (UseList()
            << Use(1, 7, 10, Highlighting::TypeUse)
            << Use(1, 24, 10, Highlighting::FieldUse)
            << Use(2, 6, 1, Highlighting::FunctionDeclarationUse)
            << Use(2, 8, 10, Highlighting::TypeUse)
            << Use(2, 19, 7, Highlighting::LocalUse)
            << Use(3, 3, 7, Highlighting::LocalUse)
            << Use(3, 11, 10, Highlighting::FieldUse));

    QTest::newRow("instantation_as_function_call_QTCREATORBUG15212")
        << _("struct Foo {};\n"
             "template <typename Type> struct test {\n"
             "  test() {}\n"
             "  test(int, int) {}\n"
             "};\n"
             "void test(int int_argument) {\n"
             "  const int very_long_constant_of_type_int = 11111111111111111;\n"
             "  test<Foo> foo1;\n"
             "  test<Foo> foo2(int_argument, int_argument);\n"
             "  test<Foo> foo3(very_long_constant_of_type_int,\n"
             "                 very_long_constant_of_type_int);\n"
             "  test<int> size1(int_argument, int_argument);\n"
             "  (void)foo1, foo2, foo3, size1;\n"
             "  test(int_argument);\n"
             "}\n")
        << (UseList()
            << Use(1, 8, 3, Highlighting::TypeUse)
            << Use(2, 20, 4, Highlighting::TypeUse)
            << Use(2, 33, 4, Highlighting::TypeUse)
            << Use(3, 3, 4, Highlighting::TypeUse)
            << Use(4, 3, 4, Highlighting::TypeUse)
            << Use(6, 6, 4, Highlighting::FunctionDeclarationUse)
            << Use(6, 15, 12, Highlighting::LocalUse)
            << Use(7, 13, 30, Highlighting::LocalUse)
            << Use(8, 3, 4, Highlighting::TypeUse)
            << Use(8, 8, 3, Highlighting::TypeUse)
            << Use(8, 13, 4, Highlighting::LocalUse)
            << Use(9, 3, 4, Highlighting::TypeUse)
            << Use(9, 8, 3, Highlighting::TypeUse)
            << Use(9, 13, 4, Highlighting::LocalUse)
            << Use(9, 18, 12, Highlighting::LocalUse)
            << Use(9, 32, 12, Highlighting::LocalUse)
            << Use(10, 3, 4, Highlighting::TypeUse)
            << Use(10, 8, 3, Highlighting::TypeUse)
            << Use(10, 13, 4, Highlighting::LocalUse)
            << Use(10, 18, 30, Highlighting::LocalUse)
            << Use(11, 18, 30, Highlighting::LocalUse)
            << Use(12, 3, 4, Highlighting::TypeUse)
            << Use(12, 13, 5, Highlighting::LocalUse)
            << Use(12, 19, 12, Highlighting::LocalUse)
            << Use(12, 33, 12, Highlighting::LocalUse)
            << Use(13, 9, 4, Highlighting::LocalUse)
            << Use(13, 15, 4, Highlighting::LocalUse)
            << Use(13, 21, 4, Highlighting::LocalUse)
            << Use(13, 27, 5, Highlighting::LocalUse)
            << Use(14, 3, 4, Highlighting::FunctionUse)
            << Use(14, 8, 12, Highlighting::LocalUse));

    QTest::newRow("unicodeIdentifier1")
        << _("class My" TEST_UNICODE_IDENTIFIER "Type { int " TEST_UNICODE_IDENTIFIER "Member; };\n"
             "void f(My" TEST_UNICODE_IDENTIFIER "Type var" TEST_UNICODE_IDENTIFIER ")\n"
             "{ var" TEST_UNICODE_IDENTIFIER "." TEST_UNICODE_IDENTIFIER "Member = 0; }\n")
        << (UseList()
            << Use(1, 7, 10, Highlighting::TypeUse)
            << Use(1, 24, 10, Highlighting::FieldUse)
            << Use(2, 6, 1, Highlighting::FunctionDeclarationUse)
            << Use(2, 8, 10, Highlighting::TypeUse)
            << Use(2, 19, 7, Highlighting::LocalUse)
            << Use(3, 3, 7, Highlighting::LocalUse)
            << Use(3, 11, 10, Highlighting::FieldUse));

    QTest::newRow("unicodeIdentifier2")
        << _("class v" TEST_UNICODE_IDENTIFIER "\n"
             "{\n"
             "public:\n"
             "    v" TEST_UNICODE_IDENTIFIER "();\n"
             "    ~v" TEST_UNICODE_IDENTIFIER "();\n"
             "};\n"
             "\n"
             "v" TEST_UNICODE_IDENTIFIER "::v" TEST_UNICODE_IDENTIFIER "() {}\n"
             "v" TEST_UNICODE_IDENTIFIER "::~v" TEST_UNICODE_IDENTIFIER "() {}\n")
        << (UseList()
            << Use(1, 7, 5, Highlighting::TypeUse)
            << Use(4, 5, 5, Highlighting::TypeUse)
            << Use(5, 6, 5, Highlighting::TypeUse)
            << Use(5, 6, 5, Highlighting::TypeUse)
            << Use(8, 1, 5, Highlighting::TypeUse)
            << Use(8, 8, 5, Highlighting::FunctionDeclarationUse)
            << Use(9, 1, 5, Highlighting::TypeUse)
            << Use(9, 1, 5, Highlighting::TypeUse)
            << Use(9, 9, 5, Highlighting::TypeUse));

#define UC_U10302_4TIMES UC_U10302 UC_U10302 UC_U10302 UC_U10302
#define UC_U10302_12TIMES UC_U10302_4TIMES UC_U10302_4TIMES UC_U10302_4TIMES
    QTest::newRow("unicodeComments1")
        << _("#define NULL 0\n"
             "\n"
             "// " UC_U10302_12TIMES "\n"
             "// " UC_U10302_12TIMES "\n"
             "\n"
             "class Foo {\n"
                 "double f(bool b = NULL);\n"
                 "Foo *x;\n"
             "};\n")
        << (UseList()
            << Use(6, 7, 3, Highlighting::TypeUse)
            << Use(7, 8, 1, Highlighting::FunctionDeclarationUse)
            << Use(8, 1, 3, Highlighting::TypeUse)
            << Use(8, 6, 1, Highlighting::FieldUse));
#undef UC_U10302_12TIMES
#undef UC_U10302_4TIMES
}

void tst_CheckSymbols::test_checksymbols_macroUses()
{
    QFETCH(QByteArray, source);
    QFETCH(UseList, expectedUsesAll);
    QFETCH(UseList, expectedUsesMacros);

    TestCase(source, expectedUsesAll, expectedUsesMacros);
}

void tst_CheckSymbols::test_checksymbols_macroUses_data()
{
    QTest::addColumn<QByteArray>("source");
    QTest::addColumn<UseList>("expectedUsesAll");
    QTest::addColumn<UseList>("expectedUsesMacros");

    QTest::newRow("MacroUse")
        << _("#define FOO 1+1\n"
             "int f() { FOO; }\n")
        << (UseList()
            << Use(1, 9, 3, Highlighting::MacroUse)
            << Use(2, 5, 1, Highlighting::FunctionDeclarationUse)
            << Use(2, 11, 3, Highlighting::MacroUse))
        << (UseList()
            << Use(1, 9, 3, Highlighting::MacroUse)
            << Use(2, 11, 3, Highlighting::MacroUse));
}

void tst_CheckSymbols::test_checksymbols_infiniteLoop()
{
    QFETCH(QByteArray, source1);
    QFETCH(QByteArray, source2);

    const QString filePath1 = QDir::tempPath() + QLatin1String("/file1.h");
    Tests::TestCase::writeFile(FilePath::fromString(filePath1), source1);

    const FilePath filePath2 = FilePath::fromString(QDir::tempPath()) / "/file2.h";
    Tests::TestCase::writeFile(filePath2, source2);

    const Document::Ptr document1 = TestCase::createDocument(filePath1, source1);
    document1->addIncludeFile(Document::Include("file2.h", filePath2, 1, Client::IncludeLocal));
    Snapshot snapshot;
    snapshot.insert(document1);
    snapshot.insert(TestCase::createDocument(filePath2.toUrlishString(), source2));

    TestCase::runCheckSymbols(document1, snapshot);
}

void tst_CheckSymbols::test_checkForValidSymbolFileId()
{
    const QByteArray contents =
        "constexpr int parent_of(const int f) { return 1; }\n"
        "\n"
        "template <typename T> struct wrapper { const T* ptr; };\n"
        "template <int> struct Dummy;\n"
        "\n"
        "namespace impl {\n"
        "    template <int f>\n"
        "    struct dummy_impl {\n"
        "        wrapper<Dummy<parent_of(f)>> parent;\n"
        "    };\n"
        "}\n"
        "\n"
        "template <int f>\n"
        "struct Dummy : impl::dummy_impl<f> {};\n"
        "\n"
        "void client()\n"
        "{\n"
        "    wrapper<Dummy<1>> a;\n"
        "    a.ptr->parent.ptr;\n"
        "}\n";

    BaseTestCase tc(contents);
}

void tst_CheckSymbols::test_parentOfBlock()
{
    const QByteArray source = "void C::f()\n"
                              "{\n"
                              "    enum E { e1 };\n"
                              "}\n";
    BaseTestCase tc(source);
}

void tst_CheckSymbols::test_infiniteLoop()
{
    const QByteArray source =
        "template <class> struct TNode;\n"
        "template <class> struct TMetaNode;\n"
        "\n"
        "template <class X>\n"
        "struct TTraits {\n"
        "   using TX        = X;\n"
        "   using TNodeType = TNode<TX>;\n"
        "};\n"
        "\n"
        "template <class X>\n"
        "struct TMetaNode {\n"
        "   using TTraitsType = TTraits<X>;\n"
        "};\n"
        "\n"
        "template <class X>\n"
        "void nonmember() {\n"
        "   using TMetaNodeType = TMetaNode<X>;\n"
        "}\n"
        "\n"
        "template <class X>\n"
        "struct TNode {\n"
        "   using TTraitsType = TTraits<X>;\n"
        "   void member();\n"
        "};\n"
        "\n"
        "template <class X>\n"
        "void TNode<X>::member() {}\n"
        ;

    BaseTestCase tc(source);
}

void tst_CheckSymbols::test_checksymbols_infiniteLoop_data()
{
    QTest::addColumn<QByteArray>("source1");
    QTest::addColumn<QByteArray>("source2");

    QTest::newRow("1")
        <<
         _("#include \"file2.h\"\n"
           "\n"
           "template<class _Elem, class _Traits>\n"
           "class basic_ios {\n"
           "        typedef basic_ostream<_Elem, _Traits> _Myos;\n"
           "};\n"
           "\n"
           "template<class _Elem, class _Traits>\n"
           "class basic_ostream {\n"
           "        typedef basic_ostream<_Elem, _Traits> _Myt;\n"
           "        typedef ostreambuf_iterator<_Elem, _Traits> _Iter;\n"
           "};\n")
        <<
         _("template<class _Elem, class _Traits>\n"
           "class basic_streambuf {\n"
           "        typedef basic_streambuf<_Elem, _Traits> _Myt;\n"
           "};\n"
           "\n"
           "template<class _Elem, class _Traits>\n"
           "class ostreambuf_iterator {\n"
           "    typedef _Traits traits_type;\n"
           "        typedef basic_streambuf<_Elem, _Traits> streambuf_type;\n"
           "        typedef basic_ostream<_Elem, _Traits> ostream_type;\n"
           "};\n")
           ;

    QTest::newRow("2")
        <<
         _("#include \"file2.h\"\n"
           "\n"
           "template<class _Ty >\n"
           "struct _List_base_types\n"
           "{\n"
           "        typedef typename _Wrap_alloc<_Alloc>::template rebind<_Ty>::other _Alty;\n"
           "        typedef typename _Alty::template rebind<_Node>::other _Alnod_type;\n"
           "};\n"
           "\n"
           "template<class _Alloc_types>\n"
           "struct _List_alloc \n"
           "{\n"
           "        const _Alloc_types::_Alnod_type& _Getal() const {}\n"
           "};\n"
           "\n"
           "template<class _Ty, class _Alloc>\n"
           "struct _List_buy : public _List_alloc< _List_base_types<_Ty> >\n"
           "{\n"
           "        void foo()\n"
           "        {\n"
           "                this->_Getal().construct(1, 2);\n"
           "                this->_Getal().deallocate(0, 1);\n"
           "        }\n"
           "};\n")
        <<
         _("template<class _Alloc>\n"
           "struct _Wrap_alloc : public _Alloc\n"
           "{\n"
           "    typedef _Alloc _Mybase;\n"
           "    template<class _Other> struct rebind { typedef _Wrap_alloc<_Other_alloc> other; };\n"
           "\n"
           "    void deallocate(pointer _Ptr, size_type _Count) {}\n"
           "    void construct(value_type *_Ptr) {}\n"
           "};\n")
           ;
}

void tst_CheckSymbols::findField()
{
    QFETCH(QByteArray, source);

    int position = source.indexOf('@');
    QVERIFY(position != -1);
    QByteArray truncated = source;
    truncated.truncate(position);
    const int line = truncated.count('\n') + 1;
    const int column = position - truncated.lastIndexOf('\n', position) + 1;
    source[position] = ' ';
    BaseTestCase tc(source);
    Use use = tc.findUse(line, column);
    QEXPECT_FAIL("pointer_indirect_specialization", "QTCREATORBUG-14141", Abort);
    QEXPECT_FAIL("pointer_indirect_specialization_typedef", "QTCREATORBUG-14141", Abort);
    QEXPECT_FAIL("pointer_indirect_specialization_double_indirection", "QTCREATORBUG-14141", Abort);
    QEXPECT_FAIL("instantiation_of_pointer_typedef_in_block", "QTCREATORBUG-14141", Abort);
    QEXPECT_FAIL("pointer_indirect_specialization_double_indirection_with_base", "QTCREATORBUG-14141", Abort);
    QEXPECT_FAIL("recursive_instantiation_of_template_type", "QTCREATORBUG-14237", Abort);
    QEXPECT_FAIL("recursive_instantiation_of_template_type_2", "QTCREATORBUG-14141", Abort);
    QVERIFY(use.isValid());
    QVERIFY(use.kind == Highlighting::FieldUse);
}

void tst_CheckSymbols::findField_data()
{
    QTest::addColumn<QByteArray>("source");

    QTest::newRow("pointer_indirect_specialization") << _(
        "template<typename T>\n"
        "struct Traits { typedef typename T::pointer pointer; };\n"
        "\n"
        "template<typename _Tp>\n"
        "struct Traits<_Tp*> { typedef _Tp *pointer; };\n"
        "\n"
        "template<typename T>\n"
        "class Temp\n"
        "{\n"
        "protected:\n"
        "   typedef Traits<T> TraitsT;\n"
        "\n"
        "public:\n"
        "   typedef typename TraitsT::pointer pointer;\n"
        "   pointer p;\n"
        "};\n"
        "\n"
        "struct Foo { int bar; };\n"
        "\n"
        "void func()\n"
        "{\n"
        "   Temp<Foo *> t;\n"
        "   t.p->@bar;\n"
        "}\n"
    );

    QTest::newRow("pointer_indirect_specialization_typedef") << _(
        "template<typename T>\n"
        "struct Traits { typedef typename T::pointer pointer; };\n"
        "\n"
        "template<typename _Tp>\n"
        "struct Traits<_Tp*> { typedef _Tp *pointer; };\n"
        "\n"
        "struct Foo { int bar; };\n"
        "\n"
        "class Temp\n"
        "{\n"
        "protected:\n"
        "   typedef Foo *FooPtr;\n"
        "   typedef Traits<FooPtr> TraitsT;\n"
        "\n"
        "public:\n"
        "   typedef typename TraitsT::pointer pointer;\n"
        "   pointer p;\n"
        "};\n"
        "\n"
        "void func()\n"
        "{\n"
        "   Temp t;\n"
        "   t.p->@bar;\n"
        "}\n"
    );

    QTest::newRow("instantiation_of_pointer_typedef_in_block") << _(
        "template<typename _Tp>\n"
        "struct Temp { _Tp p; };\n"
        "\n"
        "struct Foo { int bar; };\n"
        "\n"
        "void func()\n"
        "{\n"
        "   typedef Foo *pointer;\n"
        "   Temp<pointer> t;\n"
        "   t.p->@bar;\n"
        "}\n"
    );

    QTest::newRow("pointer_indirect_specialization_double_indirection") << _(
        "template<typename _Tp>\n"
        "struct Traits { };\n"
        "\n"
        "template<typename _Tp>\n"
        "struct Traits<_Tp*> { typedef _Tp *pointer; };\n"
        "\n"
        "struct Foo { int bar; };\n"
        "\n"
        "template<typename _Tp>\n"
        "struct IndirectT\n"
        "{\n"
        "   typedef Traits<_Tp> TraitsT;\n"
        "   typedef typename TraitsT::pointer pointer;\n"
        "   pointer p;\n"
        "};\n"
        "\n"
        "template<typename _Tp>\n"
        "struct Temp\n"
        "{\n"
        "   typedef _Tp *pointer;\n"
        "   typedef IndirectT<pointer> indirect;\n"
        "};\n"
        "\n"
        "void func()\n"
        "{\n"
        "   Temp<Foo>::indirect t;\n"
        "   t.p->@bar;\n"
        "}\n"
    );

    QTest::newRow("pointer_indirect_specialization_double_indirection_with_base") << _(
        "template<typename _Tp>\n"
        "struct Traits { };\n"
        "\n"
        "template<typename _Tp>\n"
        "struct Traits<_Tp*> { typedef _Tp *pointer; };\n"
        "\n"
        "struct Foo { int bar; };\n"
        "\n"
        "template<typename _Tp>\n"
        "struct IndirectT\n"
        "{\n"
        "   typedef Traits<_Tp> TraitsT;\n"
        "   typedef typename TraitsT::pointer pointer;\n"
        "   pointer p;\n"
        "};\n"
        "\n"
        "template<typename _Tp>\n"
        "struct TempBase { typedef _Tp *pointer; };\n"
        "\n"
        "template<typename _Tp>\n"
        "struct Temp : public TempBase<_Tp>\n"
        "{\n"
        "   typedef TempBase<_Tp> _Base;\n"
        "   typedef typename _Base::pointer pointer;\n"
        "   typedef IndirectT<pointer> indirect;\n"
        "};\n"
        "\n"
        "void func()\n"
        "{\n"
        "   Temp<Foo>::indirect t;\n"
        "   t.p->@bar;\n"
        "}\n"
    );

    QTest::newRow("recursive_instantiation_of_template_type") << _(
        "template<typename _Tp>\n"
        "struct Temp { typedef _Tp value_type; };\n"
        "\n"
        "struct Foo { int bar; };\n"
        "\n"
        "void func()\n"
        "{\n"
        "   Temp<Temp<Foo> >::value_type::value_type *p;\n"
        "   p->@bar;\n"
        "}\n"
    );

    QTest::newRow("recursive_instantiation_of_template_type_2") << _(
        "template<typename _Tp>\n"
        "struct Temp { typedef _Tp value_type; };\n"
        "\n"
        "struct Foo { int bar; };\n"
        "\n"
        "void func()\n"
        "{\n"
        "   Temp<Temp<Foo>::value_type>::value_type *p;\n"
        "   p->@bar;\n"
        "}\n"
    );
}

QTEST_APPLESS_MAIN(tst_CheckSymbols)
#include "tst_checksymbols.moc"
