/* This file is part of KDevelop
    Copyright 2006 Hamish Rodda <rodda@kde.org>
    Copyright 2007-2009 David Nolden <david.nolden.kdevelop@art-master.de>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License version 2 as published by the Free Software Foundation.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.
*/

#include "test_duchain.h"

#include <QTest>
#include <QWidget>

#include "declarationbuilder.h"
#include "usebuilder.h"
#include "cpptypes.h"
#include "templateparameterdeclaration.h"
#include "cppeditorintegrator.h"
#include "dumptypes.h"
#include "environmentmanager.h"
#include "typeutils.h"
#include "templatedeclaration.h"
#include "tokens.h"
#include "qtfunctiondeclaration.h"
#include "sourcemanipulation.h"

#include "rpp/chartools.h"
#include "rpp/pp-engine.h"
#include "rpp/preprocessor.h"

#include <language/duchain/duchain.h>
#include <language/duchain/duchainlock.h>
#include <language/duchain/topducontext.h>
#include <language/duchain/forwarddeclaration.h>
#include <language/duchain/functiondefinition.h>
#include <language/duchain/declarationid.h>
#include <language/duchain/declaration.h>
#include <language/duchain/dumpdotgraph.h>
#include <language/duchain/dumpchain.h>
#include <language/duchain/indexedstring.h>
#include <language/duchain/classdeclaration.h>
#include <language/duchain/types/alltypes.h>
#include <language/duchain/persistentsymboltable.h>
#include <language/duchain/codemodel.h>
#include <language/duchain/navigation/abstractnavigationwidget.h>
#include <language/duchain/duchainutils.h>

#include <language/codegen/coderepresentation.h>
#include <language/editor/documentrange.h>

#include <typeinfo>

#include <tests/testhelpers.h>
#include <tests/testcore.h>

using namespace KTextEditor;
using namespace TypeUtils;
using namespace KDevelop;
using namespace Cpp;
using namespace Utils;

QTEST_MAIN(TestDUChain)

Declaration* getDeclaration( AbstractType::Ptr base, TopDUContext* top ) {
  if( !base ) return 0;

  IdentifiedType* idType = dynamic_cast<IdentifiedType*>(base.unsafeData());
  if( idType ) {
    return idType->declaration(top);
  } else {
    return 0;
  }
}

#define TEST_FILE_PARSE_ONLY if (testFileParseOnly) QSKIP("Skip", SkipSingle);
TestDUChain::TestDUChain()
{
  testFileParseOnly = false;
}

void TestDUChain::initTestCase()
{
  noDef = 0;

  initShell();

  file1 = "file:///media/data/kdedev/4.0/kdevelop/languages/cpp/parser/duchain.cpp";
  file2 = "file:///media/data/kdedev/4.0/kdevelop/languages/cpp/parser/dubuilder.cpp";

  topContext = new TopDUContext(IndexedString(file1.pathOrUrl()), RangeInRevision(CursorInRevision(0,0),CursorInRevision(25,0)));
  DUChainWriteLocker lock(DUChain::lock());

  DUChain::self()->addDocumentChain(topContext);

  typeVoid = AbstractType::Ptr(new IntegralType(IntegralType::TypeVoid))->indexed();
  typeInt = AbstractType::Ptr(new IntegralType(IntegralType::TypeInt))->indexed();
  AbstractType::Ptr s(new IntegralType(IntegralType::TypeInt));
  s->setModifiers(AbstractType::ShortModifier);
  typeShort = s->indexed();
}

void TestDUChain::cleanupTestCase()
{
  //Just a few tests so there is always something cleared away
  QualifiedIdentifier id("test_bla12310915205342");
  IndexedQualifiedIdentifier indexedId(id);
  DelayedType::Ptr delayed(new DelayedType);
  delayed->setIdentifier(IndexedTypeIdentifier("frokkoflasdasotest_bla12310915205342"));
  IndexedType indexed = delayed->indexed();
  DUChain::self()->finalCleanup();

  {
  DUChainWriteLocker lock(DUChain::lock());

  DUChain::self()->removeDocumentChain(topContext);
  }

  TestCore::shutdown();
}

Declaration* TestDUChain::findDeclaration(DUContext* context, const Identifier& id, const CursorInRevision& position)
{
  QList<Declaration*> ret = context->findDeclarations(id, position);
  if (ret.count())
    return ret.first();
  return 0;
}

Declaration* TestDUChain::findDeclaration(DUContext* context, const QualifiedIdentifier& id, const CursorInRevision& position)
{
  QList<Declaration*> ret = context->findDeclarations(id, position);
  if (ret.count())
    return ret.first();
  return 0;
}

void TestDUChain::testIdentifiers()
{
  TEST_FILE_PARSE_ONLY

  QualifiedIdentifier aj("::Area::jump");
  QCOMPARE(aj.count(), 2);
  QCOMPARE(aj.explicitlyGlobal(), true);
  QCOMPARE(aj.at(0), Identifier("Area"));
  QCOMPARE(aj.at(1), Identifier("jump"));

  QualifiedIdentifier aj2 = QualifiedIdentifier("Area::jump");
  QCOMPARE(aj2.count(), 2);
  QCOMPARE(aj2.explicitlyGlobal(), false);
  QCOMPARE(aj2.at(0), Identifier("Area"));
  QCOMPARE(aj2.at(1), Identifier("jump"));
kDebug() << aj.toString() << aj2.toString();
kDebug() << aj.index() << aj2.index();
  QCOMPARE(aj == aj2, true);

  QVERIFY(QualifiedIdentifier("") == QualifiedIdentifier());
  QVERIFY(QualifiedIdentifier("").index() == QualifiedIdentifier().index());

//   QCOMPARE(aj.match(aj2), QualifiedIdentifier::ExactMatch);

  QualifiedIdentifier ajt("Area::jump::test");
  QualifiedIdentifier jt("jump::test");
  QualifiedIdentifier ajt2("Area::jump::tes");

//   QCOMPARE(aj2.match(ajt), QualifiedIdentifier::NoMatch);
//   QCOMPARE(ajt.match(aj2), QualifiedIdentifier::NoMatch);
//   //QCOMPARE(jt.match(aj2), QualifiedIdentifier::ContainedBy); ///@todo reenable this(but it fails)
//   QCOMPARE(ajt.match(jt), QualifiedIdentifier::EndsWith);

/*  QCOMPARE(aj2.match(ajt2), QualifiedIdentifier::NoMatch);
  QCOMPARE(ajt2.match(aj2), QualifiedIdentifier::NoMatch);*/
  QualifiedIdentifier t(" Area<A,B>::jump <F> ::tes<C>");
  QCOMPARE(t.count(), 3);
  QCOMPARE(t.at(0).templateIdentifiersCount(), 2u);
  QCOMPARE(t.at(1).templateIdentifiersCount(), 1u);
  QCOMPARE(t.at(2).templateIdentifiersCount(), 1u);
  QCOMPARE(t.at(0).identifier().str(), QString("Area"));
  QCOMPARE(t.at(1).identifier().str(), QString("jump"));
  QCOMPARE(t.at(2).identifier().str(), QString("tes"));

  QualifiedIdentifier op1("operator<");
  QualifiedIdentifier op2("operator<=");
  QualifiedIdentifier op3("operator>");
  QualifiedIdentifier op4("operator>=");
  QualifiedIdentifier op5("operator()");
  QualifiedIdentifier op6("operator( )");
  QCOMPARE(op1.count(), 1);
  QCOMPARE(op2.count(), 1);
  QCOMPARE(op3.count(), 1);
  QCOMPARE(op4.count(), 1);
  QCOMPARE(op5.count(), 1);
  QCOMPARE(op6.count(), 1);
  QCOMPARE(op4.toString(), QString("operator>="));
  QCOMPARE(op3.toString(), QString("operator>"));
  QCOMPARE(op1.toString(), QString("operator<"));
  QCOMPARE(op2.toString(), QString("operator<="));
  QCOMPARE(op5.toString(), QString("operator()"));
  QCOMPARE(op6.toString(), QString("operator( )"));
  QCOMPARE(QualifiedIdentifier("Area<A,B>::jump <F> ::tes<C>").index(), t.index());
  QCOMPARE(op4.index(), QualifiedIdentifier("operator>=").index());
  ///@todo create a big randomized test for the identifier repository(check that indices are the same)
}

void TestDUChain::testContextRelationships()
{
  TEST_FILE_PARSE_ONLY

  DUChainWriteLocker lock(DUChain::lock());

  QCOMPARE(DUChain::self()->chainForDocument(file1), topContext);

  DUContext* firstChild = new DUContext(RangeInRevision(CursorInRevision(4,4), CursorInRevision(10,3)), topContext);

  QCOMPARE(firstChild->parentContext(), topContext);
  QCOMPARE(firstChild->childContexts().count(), 0);
  QCOMPARE(topContext->childContexts().count(), 1);
  QCOMPARE(topContext->childContexts().last(), firstChild);

  DUContext* secondChild = new DUContext(RangeInRevision(CursorInRevision(14,4), CursorInRevision(19,3)), topContext);

  QCOMPARE(topContext->childContexts().count(), 2);
  QCOMPARE(topContext->childContexts()[1], secondChild);

  DUContext* thirdChild = new DUContext(RangeInRevision(CursorInRevision(10,4), CursorInRevision(14,3)), topContext);

  QCOMPARE(topContext->childContexts().count(), 3);
  QCOMPARE(topContext->childContexts()[1], thirdChild);

  topContext->deleteChildContextsRecursively();
  QVERIFY(topContext->childContexts().isEmpty());
}

void TestDUChain::testDeclareInt()
{
  TEST_FILE_PARSE_ONLY

  QByteArray method("int i;");

  LockedTopDUContext top = parse(method, DumpNone);

  QVERIFY(!top->parentContext());
  QCOMPARE(top->childContexts().count(), 0);
  QCOMPARE(top->localDeclarations().count(), 1);
  QVERIFY(top->localScopeIdentifier().isEmpty());

  Declaration* def = top->localDeclarations().first();
  QCOMPARE(def->identifier(), Identifier("i"));
  QCOMPARE(findDeclaration(top, def->identifier()), def);
}

void TestDUChain::testMultiByteCStrings()
{
  //                 01234567 89 012345678901234567890123456789
  QByteArray method("char* c=\"ä\";void test() { c = 1; }");
  LockedTopDUContext top = parse(method, DumpAST, 0, true);
  QVERIFY(top);
  Declaration* cDec = top->localDeclarations().first();
  QCOMPARE(cDec->uses().size(), 1);
  QCOMPARE(cDec->uses().begin()->size(), 1);
  kDebug() << cDec->uses().begin()->first();
  QEXPECT_FAIL("", "The wide ä-char is seen as two, breaking anything afterwards. see also parser test of same name", Abort);
  QVERIFY(cDec->uses().begin()->first() == RangeInRevision(0, 26, 0, 27));
}

void TestDUChain::testEllipsis()
{
  QByteArray method("void infinity(int i, ...) { }");
  LockedTopDUContext top = parse(method, DumpNone);
  Declaration* defInfinity = top->localDeclarations().first();
  QCOMPARE(defInfinity->type<FunctionType>()->arguments().count(), 2);
  QCOMPARE(defInfinity->type<FunctionType>()->arguments()[1]->toString(), QString("..."));
}

void TestDUChain::testEllipsisVexing()
{
  QByteArray method("void infinity(...);");
  LockedTopDUContext top = parse(method, DumpNone);
  Declaration* defInfinity = top->localDeclarations().first();
  QCOMPARE(defInfinity->type<FunctionType>()->arguments().count(), 1);
  QCOMPARE(defInfinity->type<FunctionType>()->arguments()[0]->toString(), QString("..."));
}

void TestDUChain::testContextSearch() {
  {
    QByteArray method("int t; struct C { }; void test() { C c; c.t = 3;}");

    LockedTopDUContext top = parse(method, DumpNone);
    QCOMPARE(top->localDeclarations().count(), 3);
    QVERIFY(top->localDeclarations()[0]->uses().isEmpty());
  }
  {
    QByteArray method("typedef union { char __size[2]; long int __align; } pthread_attr_t; struct Stru {};");

    LockedTopDUContext top = parse(method, DumpNone);
  }
}

void TestDUChain::testSeparateVariableDefinition() {
  {
    QByteArray method("struct S {static int testValue; enum { A = 5 }; class C {}; int test(C); }; int S::testValue(A); int S::test(C) {}; ");

    /**
    * A prefix-context is created that surrounds S::testValue to represent the "S" part of the scope.
    */

    LockedTopDUContext top = parse(method, DumpNone);
    QCOMPARE(top->localDeclarations().count(), 2);
    QCOMPARE(top->localDeclarations()[0]->qualifiedIdentifier(), QualifiedIdentifier("S"));
    QCOMPARE(top->childContexts().count(), 5); ///There is one 'garbage' context
    QCOMPARE(top->childContexts()[0]->localDeclarations().count(), 4);
    QCOMPARE(top->childContexts()[0]->childContexts().count(), 3);
    QCOMPARE(top->childContexts()[0]->childContexts()[0]->localDeclarations().count(), 1);
    ClassMemberDeclaration* staticDeclaration = dynamic_cast<ClassMemberDeclaration*>(top->childContexts()[0]->localDeclarations()[0]);
    QVERIFY(staticDeclaration);
    QCOMPARE(staticDeclaration->qualifiedIdentifier(), QualifiedIdentifier("S::testValue"));
    QVERIFY(staticDeclaration->isStatic());

    QCOMPARE(top->childContexts()[2]->localDeclarations().count(), 1);
    Declaration* actualDeclaration = top->childContexts()[2]->localDeclarations()[0];
    QVERIFY(!dynamic_cast<AbstractFunctionDeclaration*>(actualDeclaration));
    QCOMPARE(actualDeclaration->qualifiedIdentifier(), QualifiedIdentifier("S::testValue"));

    QCOMPARE(top->childContexts()[0]->childContexts()[0]->localDeclarations()[0]->uses().count(), 1);

    ///@todo declaration/definition relationship


    QVERIFY(dynamic_cast<AbstractFunctionDeclaration*>(top->localDeclarations()[1]));

  }

  {
    //Declaration + definition
    QByteArray method("extern int x; int x;");

    LockedTopDUContext top = parse(method, DumpNone);
    QCOMPARE(top->localDeclarations().count(), 2);
    ///@todo declaration/definition relationship
  }
}

void TestDUChain::testIntegralTypes()
{
  TEST_FILE_PARSE_ONLY

  QByteArray method("const unsigned int i, k; volatile long double j; int* l; double * const * m; const int& n = l; long long o; long long int p;");

  LockedTopDUContext top = parse(method, DumpNone);

  QVERIFY(!top->parentContext());
  QCOMPARE(top->childContexts().count(), 0);
  QCOMPARE(top->localDeclarations().count(), 8);
  QVERIFY(top->localScopeIdentifier().isEmpty());

  Declaration* defI = top->localDeclarations().first();
  QCOMPARE(defI->identifier(), Identifier("i"));
  QCOMPARE(findDeclaration(top, defI->identifier()), defI);
  QVERIFY(defI->type<IntegralType>());
  QCOMPARE(defI->type<IntegralType>()->dataType(), (uint)IntegralType::TypeInt);
  QCOMPARE(defI->type<IntegralType>()->modifiers(), (unsigned long long)(AbstractType::UnsignedModifier | AbstractType::ConstModifier));

  Declaration* defK = top->localDeclarations()[1];
  QCOMPARE(defK->identifier(), Identifier("k"));
  QCOMPARE(defK->type<IntegralType>()->indexed(), defI->type<IntegralType>()->indexed());

  Declaration* defJ = top->localDeclarations()[2];
  QCOMPARE(defJ->identifier(), Identifier("j"));
  QCOMPARE(findDeclaration(top, defJ->identifier()), defJ);
  QVERIFY(defJ->type<IntegralType>());
  QCOMPARE(defJ->type<IntegralType>()->dataType(), (uint)IntegralType::TypeDouble);
  QCOMPARE(defJ->type<IntegralType>()->modifiers(), (unsigned long long)AbstractType::LongModifier | (unsigned long long)AbstractType::VolatileModifier);

  Declaration* defL = top->localDeclarations()[3];
  QCOMPARE(defL->identifier(), Identifier("l"));
  QVERIFY(defL->type<PointerType>());
  QCOMPARE(defL->type<PointerType>()->baseType()->indexed(), typeInt);
  QCOMPARE(defL->type<PointerType>()->modifiers(), (unsigned long long)AbstractType::NoModifiers);

  Declaration* defM = top->localDeclarations()[4];
  QCOMPARE(defM->identifier(), Identifier("m"));
  PointerType::Ptr firstpointer = defM->type<PointerType>();
  QVERIFY(firstpointer);
  QCOMPARE(firstpointer->modifiers(), (unsigned long long)AbstractType::NoModifiers);
  PointerType::Ptr secondpointer = PointerType::Ptr::dynamicCast(firstpointer->baseType());
  QVERIFY(secondpointer);
  QCOMPARE(secondpointer->modifiers(), (unsigned long long)AbstractType::ConstModifier);
  IntegralType::Ptr base = IntegralType::Ptr::dynamicCast(secondpointer->baseType());
  QVERIFY(base);
  QCOMPARE(base->dataType(), (uint)IntegralType::TypeDouble);
  QCOMPARE(base->modifiers(), (unsigned long long)AbstractType::NoModifiers);

  Declaration* defN = top->localDeclarations()[5];
  QCOMPARE(defN->identifier(), Identifier("n"));
  QVERIFY(defN->type<ReferenceType>());
  base = IntegralType::Ptr::dynamicCast(defN->type<ReferenceType>()->baseType());
  QVERIFY(base);
  QCOMPARE(base->dataType(), (uint)IntegralType::TypeInt);
  QCOMPARE(base->modifiers(), (unsigned long long)AbstractType::ConstModifier);

  Declaration* defO = top->localDeclarations()[6];
  QCOMPARE(defO->identifier(), Identifier("o"));
  QVERIFY(defO->type<IntegralType>());
  QCOMPARE(defO->type<IntegralType>()->modifiers(),
           (unsigned long long)AbstractType::LongLongModifier |
           (unsigned long long)AbstractType::LongModifier);
  QCOMPARE(defO->abstractType()->toString(), QString("long long int"));

  Declaration* defP = top->localDeclarations()[7];
  QCOMPARE(defP->identifier(), Identifier("p"));
  QVERIFY(defP->type<IntegralType>());
  QCOMPARE(defP->type<IntegralType>()->modifiers(),
           defO->type<IntegralType>()->modifiers());
  QCOMPARE(defP->abstractType()->toString(), QString("long long int"));

  //Even if reference/pointer types have an invalid target, they should still preserve the pointer
  QVERIFY(AbstractType::Ptr(new ReferenceType)->toString().endsWith("&"));
  QVERIFY(AbstractType::Ptr(new PointerType)->toString().endsWith("*"));
  QVERIFY(AbstractType::Ptr(new PtrToMemberType)->toString().endsWith("::*"));

}

void TestDUChain::testConversionReturn() {
  TEST_FILE_PARSE_ONLY

  QByteArray method("class A {}; class B{ operator A*() const ; };");

  LockedTopDUContext top = parse(method, DumpNone);

  QVERIFY(!top->parentContext());
  QCOMPARE(top->childContexts().count(), 2);
  QCOMPARE(top->localDeclarations().count(), 2);

  QCOMPARE(top->childContexts()[1]->localDeclarations().count(), 1);
  FunctionType::Ptr funType = top->childContexts()[1]->localDeclarations()[0]->type<KDevelop::FunctionType>();
  QVERIFY(funType);
  QVERIFY(funType->returnType().cast<PointerType>());

}

void TestDUChain::testTypeof() {
  TEST_FILE_PARSE_ONLY

  {
    QByteArray method("__typeof__(wchar_t) x; typedef typeof(++x) x_t;");

    LockedTopDUContext top = parse(method, DumpNone);

    QCOMPARE(top->localDeclarations().count(), 2);
    QCOMPARE(unAliasedType(top->localDeclarations()[1]->abstractType())->toString(), QString("wchar_t"));
  }
}

void TestDUChain::testArrayType()
{
  TEST_FILE_PARSE_ONLY

  QByteArray method("const unsigned int ArraySize = 3; int i[ArraySize];");

  LockedTopDUContext top = parse(method, DumpNone);

  QVERIFY(!top->parentContext());
  QCOMPARE(top->childContexts().count(), 0);
  QCOMPARE(top->localDeclarations().count(), 2);
  QVERIFY(top->localScopeIdentifier().isEmpty());

  kDebug() << top->localDeclarations()[0]->abstractType()->toString();
  QVERIFY(top->localDeclarations()[0]->uses().size());
  QVERIFY(top->localDeclarations()[0]->type<ConstantIntegralType>());

  Declaration* defI = top->localDeclarations().last();
  QCOMPARE(defI->identifier(), Identifier("i"));
  QCOMPARE(findDeclaration(top, defI->identifier()), defI);

  ArrayType::Ptr array = defI->type<ArrayType>();
  QVERIFY(array);
  IntegralType::Ptr element = IntegralType::Ptr::dynamicCast(array->elementType());
  QVERIFY(element);
  QCOMPARE(element->dataType(), (uint)IntegralType::TypeInt);
  QCOMPARE(array->dimension(), 3);

}

void TestDUChain::testProblematicUses()
{
  TEST_FILE_PARSE_ONLY

  {
    QByteArray method("struct A { A() : a( 0 ) { } int a; };");

    LockedTopDUContext top = parse(method, DumpAll);

    QCOMPARE(top->childContexts().count(), 1);
    QCOMPARE(top->localDeclarations().count(), 1);
    QCOMPARE(top->childContexts()[0]->localDeclarations().size(), 2); //a uses
    QVERIFY(!top->childContexts()[0]->localDeclarations()[1]->uses().isEmpty());
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().begin()->size(), 1);
  }

  {
    QByteArray method("struct A { A() : a( 0 ) { } int a; } r;");

    LockedTopDUContext top = parse(method, DumpAll);

    QCOMPARE(top->childContexts().count(), 1);
    QCOMPARE(top->localDeclarations().count(), 2);
    QCOMPARE(top->childContexts()[0]->localDeclarations().size(), 2); //a uses
    QVERIFY(!top->childContexts()[0]->localDeclarations()[1]->uses().isEmpty());
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().begin()->size(), 1);
  }

  {
    QByteArray method("struct S { int a; }; int c; int q; int a; S* s; void test() { if(s->a < q && a > c) { } if(s->a < q && a > c) { } }");

    LockedTopDUContext top = parse(method, DumpNone);

    QCOMPARE(top->childContexts().count(), 3);
    QCOMPARE(top->localDeclarations().count(), 6);
    QCOMPARE(top->localDeclarations()[1]->uses().size(), 1);
    QCOMPARE(top->localDeclarations()[1]->uses().begin()->size(), 2); //c uses
    QCOMPARE(top->localDeclarations()[2]->uses().size(), 1);
    QCOMPARE(top->localDeclarations()[2]->uses().begin()->size(), 2); //q uses
    QCOMPARE(top->localDeclarations()[3]->uses().size(), 1);
    QCOMPARE(top->localDeclarations()[3]->uses().begin()->size(), 2); //a uses
    QCOMPARE(top->childContexts()[0]->localDeclarations()[0]->uses().begin()->size(), 2); //a uses

  }

{
    //                 0         1         2         3         4         5
    //                 012345678901234567890123456789012345678901234567890123456789
    QByteArray method("enum { b = 5 }; template<bool b> struct A { enum { Mem }; }; void test(int a) { if(a < 0 && a > -1) { } if(a < 0 && a < -1) { } if(A<(b < 10 && b < 0)>::Mem) { } if(A<(b < 10 && b > 0)>::Mem) { } }");

///@todo This works in g++, but not in kdevelops parser. Although it's butt ugly.
//     if(A<b < 10 && b < 0>::Mem) {}

    LockedTopDUContext top = parse(method, DumpNone);

    QCOMPARE(top->childContexts().count(), 5);
    QCOMPARE(top->childContexts()[3]->localDeclarations().count(), 1);
    QCOMPARE(top->childContexts()[3]->localDeclarations()[0]->uses().size(), 1);
    QCOMPARE(top->childContexts()[3]->localDeclarations()[0]->uses().begin()->size(), 4); //a uses

    QCOMPARE(top->childContexts()[0]->localDeclarations().count(), 1);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[0]->uses().begin()->size(), 4); //b uses

  }

  {
    QByteArray method("struct c { void foo(int x); }; struct f { void func(); }; struct e { f getF(); }; void test() { c* c_; e* e_; c_->foo(e_->getF().func());");
    LockedTopDUContext top = parse(method, DumpNone);
    QCOMPARE(top->childContexts().size(), 5);
    QCOMPARE(top->childContexts()[1]->localDeclarations().size(), 1);
    QCOMPARE(top->childContexts()[1]->localDeclarations()[0]->uses().size(), 1);
  }
}

void TestDUChain::testBaseUses()
{
  TEST_FILE_PARSE_ONLY

{
    //                 0         1         2         3         4         5         6         7         8
    //                 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
    QByteArray method("namespace N { struct A { A(int); int m; }; }; struct B : public N::A { B() : N::A(5) { this->N::A::m = 5; } };");

    LockedTopDUContext top = parse(method, DumpNone);

    QVERIFY(top->problems().isEmpty()); //N::A must be found in the constructor
    QCOMPARE(top->childContexts().count(), 2);
    QCOMPARE(top->childContexts()[0]->localDeclarations().count(), 1);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[0]->uses().size(), 1);
    QCOMPARE(top->childContexts()[0]->childContexts().count(), 1);
    QCOMPARE(top->childContexts()[0]->childContexts()[0]->localDeclarations().count(), 2);
    QVERIFY(!top->childContexts()[0]->childContexts()[0]->localDeclarations()[1]->uses().isEmpty());
    QCOMPARE(top->childContexts()[0]->localDeclarations()[0]->uses().begin()->size(), 3);
    // the ctor usage
    QCOMPARE(top->childContexts()[0]->childContexts()[0]->localDeclarations()[0]->uses().count(), 1);
    QCOMPARE(top->childContexts()[0]->childContexts()[0]->localDeclarations()[0]->uses().values().first().count(), 1);
    QCOMPARE(top->childContexts()[0]->childContexts()[0]->localDeclarations()[0]->uses().values().first().first(), RangeInRevision(0, 81, 0, 82));
  }

  {
    //                 0         1         2         3         4         5
    //                 012345678901234567890123456789012345678901234567890123456789
    QByteArray method("class A{ class B {}; }; class C : public A::B { C() : A::B() {} };");

    LockedTopDUContext top = parse(method, DumpNone);

    QCOMPARE(top->localDeclarations().count(), 2);
    QCOMPARE(top->childContexts().count(), 2);
    QCOMPARE(top->childContexts()[0]->localDeclarations().count(), 1);
    QCOMPARE(top->childContexts()[1]->usesCount(), 2);

    QCOMPARE(top->childContexts()[1]->uses()[0].m_range, RangeInRevision(0, 41, 0, 42));
    QCOMPARE(top->childContexts()[1]->uses()[1].m_range, RangeInRevision(0, 44, 0, 45));

    QCOMPARE(top->localDeclarations()[0]->uses().count(), 1);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[0]->uses().count(), 1);

    QCOMPARE(top->childContexts()[1]->childContexts().count(), 2);
    QCOMPARE(top->childContexts()[1]->childContexts()[1]->usesCount(), 2);

  }
  {
    //                 0         1         2         3         4         5
    //                 012345678901234567890123456789012345678901234567890123456789
    QByteArray method("template<typename T> class A{ A(){} };\n"
                      "template<typename T> class B : public A<T> { B() : A<T>() {} };");

    LockedTopDUContext top = parse(method, DumpNone);

    QCOMPARE(top->localDeclarations().count(), 2);

    // uses of class A
    QEXPECT_FAIL("", "Cookie for the one fixing that!", Abort);
    QCOMPARE(top->localDeclarations().first()->uses().size(), 1);
    QCOMPARE(top->localDeclarations().first()->uses().begin()->size(), 2);
    // use in class B : public A<T>
    QCOMPARE(top->localDeclarations().first()->uses().begin()->at(0), RangeInRevision(1, 38, 1, 39));
    // use in B() : A<T>()
    QCOMPARE(top->localDeclarations().first()->uses().begin()->at(1), RangeInRevision(1, 51, 1, 52));

    // use of A's ctor
    QCOMPARE(top->childContexts()[0]->localDeclarations().count(), 1);
    QCOMPARE(top->childContexts()[1]->localDeclarations().first()->uses().size(), 1);
    QCOMPARE(top->childContexts()[1]->localDeclarations().first()->uses().begin()->size(), 1);
    QCOMPARE(top->childContexts()[1]->localDeclarations().first()->uses().begin()->first(), RangeInRevision(1, 55, 1, 56));

  }
}


void TestDUChain::testTypedefUses()
{
  {

    //                 0         1         2         3         4         5
    //                 012345678901234567890123456789012345678901234567890123456789
    QByteArray method("namespace Search { typedef int StateHypothesisIndexwqeqrqwe; StateHypothesisIndexwqeqrqwe i; }");

    LockedTopDUContext top = parse(method, DumpNone);

    QCOMPARE(top->localDeclarations().count(), 1);
    QCOMPARE(top->childContexts().count(), 1);
    QCOMPARE(top->childContexts()[0]->usesCount(), 1);
    QCOMPARE(top->childContexts()[0]->localDeclarations().count(), 2);
    QVERIFY(top->childContexts()[0]->localDeclarations()[0]->inSymbolTable());
    QCOMPARE(top->childContexts()[0]->localDeclarations()[0]->uses().size(), 1);
    QCOMPARE(unAliasedType(top->childContexts()[0]->localDeclarations()[1]->abstractType())->toString(), QString("int"));

  }
  {
    //                 0         1         2         3         4         5
    //                 012345678901234567890123456789012345678901234567890123456789
    QByteArray method("class A{}; typedef A B; A c; B d;");

    LockedTopDUContext top = parse(method, DumpNone);

    QCOMPARE(top->localDeclarations().count(), 4);
    QCOMPARE(top->usesCount(), 3);
    QCOMPARE(top->localDeclarations()[0]->uses().count(), 1); //1 File
    QCOMPARE(top->localDeclarations()[1]->uses().count(), 1); //1 File

    QCOMPARE(top->localDeclarations()[0]->uses().begin()->count(), 2); //Typedef and "A c;"
    QCOMPARE(top->localDeclarations()[1]->uses().begin()->count(), 1); //"B d;"

  }
}

void TestDUChain::testConstructorOperatorUses()
{
  TEST_FILE_PARSE_ONLY

  //                 0         1         2         3         4         5         6         7         8
  //                 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
  QByteArray method("class A{A(int); int operator()( ) {}; }; void test() { A a(1); A b = A(2); b(); }");

  LockedTopDUContext top = parse(method, DumpNone);

  QCOMPARE(top->localDeclarations().count(), 2);
//   QCOMPARE(top->usesCount(), 3);
  QCOMPARE(top->childContexts().count(), 3);
  QCOMPARE(top->childContexts()[2]->localDeclarations().count(), 2);

  QCOMPARE(top->childContexts()[2]->localDeclarations()[1]->uses().count(), 1);

  QCOMPARE(top->localDeclarations()[0]->uses().count(), 1);
  QCOMPARE(top->localDeclarations()[0]->uses().begin()->count(), 3);
  QCOMPARE(top->localDeclarations()[0]->uses().begin()->at(0), RangeInRevision(0, 55, 0, 56));
  QCOMPARE(top->localDeclarations()[0]->uses().begin()->at(1), RangeInRevision(0, 63, 0, 64));
  QCOMPARE(top->localDeclarations()[0]->uses().begin()->at(2), RangeInRevision(0, 69, 0, 70));

  QCOMPARE(top->childContexts()[0]->localDeclarations().count(), 2);
  QCOMPARE(top->childContexts()[0]->localDeclarations()[0]->uses().count(), 1);

  QCOMPARE(top->childContexts()[0]->localDeclarations()[0]->uses().begin()->count(), 2);
  QCOMPARE(top->childContexts()[0]->localDeclarations()[0]->uses().begin()->at(0), RangeInRevision(0, 58, 0, 59));
  QCOMPARE(top->childContexts()[0]->localDeclarations()[0]->uses().begin()->at(1), RangeInRevision(0, 70, 0, 71));
  
  QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().count(), 1);
  QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().begin()->count(), 1);
  QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().begin()->at(0), RangeInRevision(0, 76, 0, 77));
  
}
void TestDUChain::testDeclareFor()
{
  TEST_FILE_PARSE_ONLY

  //                 0         1         2         3         4         5
  //                 012345678901234567890123456789012345678901234567890123456789
  QByteArray method("int main() { for (int i = 0; i < 10; i++) { if (i == 4) return; int i5[5]; i5[i] = 1; } }");

  LockedTopDUContext top = parse(method, DumpNone);

  QVERIFY(!top->parentContext());
  QCOMPARE(top->childContexts().count(), 2);
  QCOMPARE(top->localDeclarations().count(), 1);
  QVERIFY(top->localScopeIdentifier().isEmpty());

  Declaration* defMain = top->localDeclarations().first();
  QCOMPARE(defMain->identifier(), Identifier("main"));
  QCOMPARE(findDeclaration(top, defMain->identifier()), defMain);
  QVERIFY(defMain->type<FunctionType>());
  QCOMPARE(defMain->type<FunctionType>()->returnType()->indexed(), typeInt);
  QCOMPARE(defMain->type<FunctionType>()->arguments().count(), 0);
  QCOMPARE(defMain->type<FunctionType>()->modifiers(), (unsigned long long)AbstractType::NoModifiers);

  QCOMPARE(findDeclaration(top, Identifier("i")), noDef);

  DUContext* main = top->childContexts()[1];
  QVERIFY(main->parentContext());
  QCOMPARE(main->importedParentContexts().count(), 1);
  QCOMPARE(main->childContexts().count(), 2);
  QCOMPARE(main->localDeclarations().count(), 0);
  QCOMPARE(main->localScopeIdentifier(), QualifiedIdentifier("main"));

  QCOMPARE(findDeclaration(main, Identifier("i")), noDef);

  DUContext* forCtx = main->childContexts()[1];
  QVERIFY(forCtx->parentContext());
  QCOMPARE(forCtx->importedParentContexts().count(), 1);
  QCOMPARE(forCtx->childContexts().count(), 2);
  QCOMPARE(forCtx->localDeclarations().count(), 1);
  QVERIFY(forCtx->localScopeIdentifier().isEmpty());

  DUContext* forParamCtx = forCtx->importedParentContexts().first().context(0);
  QVERIFY(forParamCtx->parentContext());
  QCOMPARE(forParamCtx->importedParentContexts().count(), 0);
  QCOMPARE(forParamCtx->childContexts().count(), 0);
  QCOMPARE(forParamCtx->localDeclarations().count(), 1);
  QVERIFY(forParamCtx->localScopeIdentifier().isEmpty());

  Declaration* defI = forParamCtx->localDeclarations().first();
  QCOMPARE(defI->identifier(), Identifier("i"));
  QCOMPARE(defI->uses().count(), 1);
  QCOMPARE(defI->uses().begin()->count(), 4);

  QCOMPARE(findDeclaration(forCtx, defI->identifier()), defI);

  DUContext* ifCtx = forCtx->childContexts()[1];
  QVERIFY(ifCtx->parentContext());
  QCOMPARE(ifCtx->importedParentContexts().count(), 1);
  QCOMPARE(ifCtx->childContexts().count(), 0);
  QCOMPARE(ifCtx->localDeclarations().count(), 0);
  QVERIFY(ifCtx->localScopeIdentifier().isEmpty());

  QCOMPARE(findDeclaration(ifCtx,  defI->identifier()), defI);

}


void TestDUChain::testEnum()
{
  TEST_FILE_PARSE_ONLY

/*                   0         1         2         3         4         5         6         7
                     01234567890123456789012345678901234567890123456789012345678901234567890123456789*/
  QByteArray method("enum Enum { Value1 = 5 //Comment\n, value2 //Comment1\n }; enum Enum2 { Value21, value22 = 0x02 }; union { int u1; float u2; };");

  LockedTopDUContext top = parse(method, DumpNone);
/*  {
    Declaration* decl = findDeclaration(top, QualifiedIdentifier("Value1"));
    QVERIFY(decl);
    AbstractType::Ptr t = decl->abstractType();
    QVERIFY(t);
    IdentifiedType* id = dynamic_cast<IdentifiedType*>(t.unsafeData());
    QVERIFY(id);
    QCOMPARE(id->declaration(top), decl);
  }*/

  QCOMPARE(top->localDeclarations().count(), 3);
  QCOMPARE(top->childContexts().count(), 3);

  QVERIFY(top->childContexts()[2]->inSymbolTable());

  ///@todo Test for the enum ranges and fix them, they overlap
  kDebug() << top->childContexts()[0]->range().castToSimpleRange().textRange();
  kDebug() << top->localDeclarations()[0]->range().castToSimpleRange().textRange();

  Declaration* decl = findDeclaration(top, Identifier("Enum"));
  Declaration* enumDecl = decl;
  QVERIFY(decl);
  QVERIFY(decl->internalContext());
  AbstractType::Ptr t = decl->abstractType();
  QVERIFY(dynamic_cast<EnumerationType*>(t.unsafeData()));
  EnumerationType* en = static_cast<EnumerationType*>(t.unsafeData());
  QVERIFY(en->declaration(top));
  QCOMPARE(en->qualifiedIdentifier(), QualifiedIdentifier("Enum"));
  Declaration* EnumDecl = decl;

  {
    QVERIFY(!top->findLocalDeclarations(Identifier("Value1")).isEmpty());

    decl = findDeclaration(top, QualifiedIdentifier("Value1"));
    QVERIFY(decl);

    QCOMPARE(decl->context()->owner(), enumDecl);
//     QCOMPARE(decl->context()->scopeIdentifier(), QualifiedIdentifier("Enum"));


    t = decl->abstractType();
    QVERIFY(dynamic_cast<EnumeratorType*>(t.unsafeData()));
    EnumeratorType* en = static_cast<EnumeratorType*>(t.unsafeData());
    QCOMPARE((int)en->value<qint64>(), 5);
    kDebug() << decl->qualifiedIdentifier().toString();
    kDebug() << en->toString();
    QCOMPARE(en->declaration(top), top->childContexts()[0]->localDeclarations()[0]);

    decl = findDeclaration(top, Identifier("value2"));
    QVERIFY(decl);
    t = decl->abstractType();
    QVERIFY(dynamic_cast<EnumeratorType*>(t.unsafeData()));
    en = static_cast<EnumeratorType*>(t.unsafeData());
    QCOMPARE((int)en->value<qint64>(), 6);
    QCOMPARE(en->declaration(top), top->childContexts()[0]->localDeclarations()[1]);
    QCOMPARE(en->declaration(top)->context()->owner(), EnumDecl);
  }
  decl = findDeclaration(top, Identifier("Enum2"));
  QVERIFY(decl);
  t = decl->abstractType();
  QVERIFY(dynamic_cast<EnumerationType*>(t.unsafeData()));
  en = static_cast<EnumerationType*>(t.unsafeData());
  QVERIFY(en->declaration(top));
  QCOMPARE(en->qualifiedIdentifier(), QualifiedIdentifier("Enum2"));
  Declaration* Enum2Decl = decl;

  {
    decl = findDeclaration(top, Identifier("Value21"));
    QVERIFY(decl);
    t = decl->abstractType();
    QVERIFY(dynamic_cast<EnumeratorType*>(t.unsafeData()));
    EnumeratorType* en = static_cast<EnumeratorType*>(t.unsafeData());
    QCOMPARE((int)en->value<qint64>(), 0);
    QCOMPARE(en->declaration(top)->context()->owner(), Enum2Decl);

    decl = findDeclaration(top, Identifier("value22"));
    QVERIFY(decl);
    t = decl->abstractType();
    QVERIFY(dynamic_cast<EnumeratorType*>(t.unsafeData()));
    en = static_cast<EnumeratorType*>(t.unsafeData());
    QCOMPARE((int)en->value<qint64>(), 2);
    QCOMPARE(en->declaration(top)->context()->owner(), Enum2Decl);
  }
  {
    //Verify that the union members were propagated up
    decl = findDeclaration(top, Identifier("u1"));
    QVERIFY(decl);

    decl = findDeclaration(top, Identifier("u2"));
    QVERIFY(decl);
  }
}

/*! Custom assertion which verifies that @p topcontext contains a
 *  single class with a single memberfunction declaration in it.
 *  @p memberFun is an output parameter and should have type
 *  pointer to ClassFunctionDeclaration */
#define ASSERT_SINGLE_MEMBER_FUNCTION_IN(topcontext, memberFun) \
{ \
    QVERIFY(top);\
    QCOMPARE(1, top->childContexts().count());\
    QCOMPARE(1, top->localDeclarations().count()); \
    DUContext* clazzCtx = top->childContexts()[0]; \
    QCOMPARE(1, clazzCtx->localDeclarations().count()); \
    Declaration* member = clazzCtx->localDeclarations()[0]; \
    memberFun = dynamic_cast<ClassFunctionDeclaration*>(member); \
    QVERIFY(memberFun); \
} (void)(0)

/*! Custom assertion which verifies that @p topcontext contains a
 *  single class with two memberfunction declarations in it.
 *  @p memberFun1 and @p memberFUn2 are output parameter and should
 *  have type pointer to ClassFunctionDeclaration */
#define ASSERT_TWO_MEMBER_FUNCTIONS_IN(topcontext, memberFun1, memberFun2) \
{ \
    QVERIFY(top);\
    QCOMPARE(1, top->childContexts().count());\
    QCOMPARE(1, top->localDeclarations().count()); \
    DUContext* clazzCtx = top->childContexts()[0]; \
    QCOMPARE(2, clazzCtx->localDeclarations().count()); \
    Declaration* member = clazzCtx->localDeclarations()[0]; \
    memberFun1 = dynamic_cast<ClassFunctionDeclaration*>(member); \
    QVERIFY(memberFun1); \
    member = clazzCtx->localDeclarations()[1]; \
    memberFun2 = dynamic_cast<ClassFunctionDeclaration*>(member); \
    QVERIFY(memberFun2); \
} (void)(0)

void TestDUChain::testVirtualMemberFunction()
{
  {
    QByteArray text("class Foo { public: virtual void bar(const float i, const int i) = 0; }; \n");
    LockedTopDUContext top = parse(text, DumpAll);

    ClassFunctionDeclaration* memberFun; // filled by assert macro below
    ASSERT_SINGLE_MEMBER_FUNCTION_IN(top, memberFun);
    QVERIFY(memberFun->isVirtual());
    QVERIFY(memberFun->isAbstract());
    FunctionType::Ptr funType = memberFun->abstractType().cast<FunctionType>();
    QVERIFY(funType);
    QCOMPARE(funType->arguments().size(), 2);
    QCOMPARE(funType->arguments()[0]->toString(), QString("const float"));
    QCOMPARE(funType->arguments()[1]->toString(), QString("const int"));
  }
  {
    QByteArray text("class Foo { public: virtual void bar(); }; \n");
    LockedTopDUContext top = parse(text, DumpNone);

    ClassFunctionDeclaration* memberFun; // filled by assert macro below
    ASSERT_SINGLE_MEMBER_FUNCTION_IN(top, memberFun);
    QVERIFY(memberFun->isVirtual());
    QVERIFY(!memberFun->isAbstract());
  }
  {
    //Forward-declarations with "struct" or "class" are considered equal, so make sure the override is detected correctly.
    QByteArray text("class S; struct A { virtual S* ret(); }; struct S { }; struct B : public A { virtual S* ret(); };");
    LockedTopDUContext top = parse(text, DumpNone);

    QCOMPARE(top->childContexts().count(), 3);
    QCOMPARE(top->localDeclarations().count(), 4);
    QCOMPARE(top->localDeclarations()[0]->indexedType(), top->localDeclarations()[2]->indexedType());
    QCOMPARE(top->childContexts()[2]->localDeclarations().count(), 1);
    QVERIFY(DUChainUtils::getOverridden(top->childContexts()[2]->localDeclarations()[0]));

  }
}

void TestDUChain::testMultipleVirtual()
{
    QByteArray text("class Foo { public: virtual void bar(); virtual void baz(); }; \n");
    LockedTopDUContext top = parse(text, DumpNone);

    ClassFunctionDeclaration *bar, *baz; // filled by assert macro below
    ASSERT_TWO_MEMBER_FUNCTIONS_IN(top, bar, baz);
    QVERIFY(bar->isVirtual());
    QVERIFY(baz->isVirtual());
    QVERIFY(!baz->isAbstract());
}

void TestDUChain::testMixedVirtualNormal()
{
  {
    QByteArray text("class Foo { public: virtual void bar(); void baz(); }; \n");
    LockedTopDUContext top = parse(text, DumpNone);

    ClassFunctionDeclaration *bar, *baz; // filled by assert macro below
    ASSERT_TWO_MEMBER_FUNCTIONS_IN(top, bar, baz);
    QVERIFY(bar->isVirtual());
    QVERIFY(!baz->isVirtual());
    QVERIFY(!baz->isAbstract());
  }
}

void TestDUChain::testNonVirtualMemberFunction()
{
  {
    QByteArray text("class Foo \n { public: void bar(); };\n");
    LockedTopDUContext top = parse(text, DumpNone);

    ClassFunctionDeclaration* memberFun; // filled by assert macro below
    ASSERT_SINGLE_MEMBER_FUNCTION_IN(top, memberFun);
    QVERIFY(!memberFun->isVirtual());
    QVERIFY(!memberFun->isAbstract());
  }
}

/*! Extract memberfunction from @p topcontext in the @p nrofClass 'th class
 *  declaration. Assert that it is named @p expectedName. Fill the outputparameter
 *  @p memberFun */
#define FETCH_MEMBER_FUNCTION(nrofClass, topcontext, expectedName, memberFun) \
{ \
    QVERIFY(top);\
    DUContext* clazzCtx = top->childContexts()[nrofClass]; \
    QCOMPARE(1, clazzCtx->localDeclarations().count()); \
    Declaration* member = clazzCtx->localDeclarations()[0]; \
    memberFun = dynamic_cast<ClassFunctionDeclaration*>(member); \
    QVERIFY(memberFun); \
    QCOMPARE(QString(expectedName), memberFun->toString()); \
} (void)(0)

void TestDUChain::testMemberFunctionModifiers()
{
  // NOTE this only verifies
  //      {no member function modifiers in code} => {no modifiers in duchain}
  // it does not check that (but probably should, with all permutations ;))
  //      {member function modifiers in code} => {exactly the same modifiers in duchain}
  //      {illegal combinations, eg explicit not on a constructor} => {error/warning}
  {
      QByteArray text("class FuBarr { void foo(); };\n"
                      "class ZooLoo { void bar(); };\n\n"
                      "class MooFoo \n { public: void loo(); };\n"
                      "class GooRoo { void baz(); };\n"
                      "class PooYoo { \n void zoo(); };\n");
      // at one point extra '\n' characters would affect these modifiers.
      LockedTopDUContext top = parse(text, DumpNone);

      ClassFunctionDeclaration *foo, *bar, *loo, *baz, *zoo; // filled below
      FETCH_MEMBER_FUNCTION(0, top, "void foo ()", foo);
      FETCH_MEMBER_FUNCTION(1, top, "void bar ()", bar);
      FETCH_MEMBER_FUNCTION(2, top, "void loo ()", loo);
      FETCH_MEMBER_FUNCTION(3, top, "void baz ()", baz);
      FETCH_MEMBER_FUNCTION(4, top, "void zoo ()", zoo);

      assertNoMemberFunctionModifiers(foo);
      assertNoMemberFunctionModifiers(bar);
      assertNoMemberFunctionModifiers(loo);
      assertNoMemberFunctionModifiers(baz);
      assertNoMemberFunctionModifiers(zoo);

  }

}

void TestDUChain::assertNoMemberFunctionModifiers(ClassFunctionDeclaration* memberFun)
{
    AbstractType::Ptr t(memberFun->abstractType());
    Q_ASSERT(t);
    bool isConstant = t->modifiers() & AbstractType::ConstModifier;
    bool isVolatile = t->modifiers() & AbstractType::VolatileModifier;

    kDebug() << memberFun->toString() << "virtual?"  << memberFun->isVirtual()
                                      << "explicit?" << memberFun->isExplicit()
                                      << "inline?"   << memberFun->isInline()
                                      << "constant?" << isConstant
                                      << "volatile?" << isVolatile
                                      << "static?"   << memberFun->isStatic();
    QVERIFY(!memberFun->isVirtual());
    QVERIFY(!memberFun->isExplicit());
    QVERIFY(!memberFun->isInline());
    QVERIFY(!isConstant);
    QVERIFY(!isVolatile);
    QVERIFY(!memberFun->isStatic());
}


void TestDUChain::testAssignedContexts()
{
  {
    QByteArray method("void test() { int i; if(i) { int q; }  int c; while(c) { char q; } int w; { float q;  } }");

    LockedTopDUContext top = parse(method, DumpNone);

    QCOMPARE(top->childContexts().count(), 2);
    QCOMPARE(top->childContexts()[1]->type(), DUContext::Other);
    QCOMPARE(top->childContexts()[1]->childContexts().count(), 4);
    QCOMPARE(top->childContexts()[1]->localDeclarations().count(), 3);
    QVERIFY(!top->childContexts()[1]->childContexts()[0]->owner());
    QVERIFY(!top->childContexts()[1]->childContexts()[1]->owner());
    QVERIFY(!top->childContexts()[1]->childContexts()[2]->owner());
    QVERIFY(!top->childContexts()[1]->childContexts()[3]->owner());
  }
}

void TestDUChain::testTryCatch() {
  TEST_FILE_PARSE_ONLY
  {
    QByteArray method("void test() { try{ int o = 3; o += 3; } catch (...) { } }");

    LockedTopDUContext top = parse(method, DumpNone);

    QCOMPARE(top->childContexts().count(), 2);
    QCOMPARE(top->childContexts()[1]->type(), DUContext::Other);
    QCOMPARE(top->childContexts()[1]->childContexts().count(), 2);
    QCOMPARE(top->childContexts()[1]->childContexts()[0]->usesCount(), 1);
  }
}

void TestDUChain::testDeclareStruct()
{
  TEST_FILE_PARSE_ONLY
//   {
//     QByteArray method("struct { short i; } instance;");
//
//     LockedTopDUContext top = parse(method, DumpNone);
//
//     QVERIFY(!top->parentContext());
//     QCOMPARE(top->childContexts().count(), 1);
//     QCOMPARE(top->localDeclarations().count(), 2);
//     QVERIFY(top->localScopeIdentifier().isEmpty());
//
//     AbstractType::Ptr t = top->localDeclarations()[0]->abstractType();
//     IdentifiedType* idType = dynamic_cast<IdentifiedType*>(t.unsafeData());
//     QVERIFY(idType);
//     QVERIFY(idType->qualifiedIdentifier().count() == 1);
//     QVERIFY(idType->qualifiedIdentifier().at(0).uniqueToken());
//
//     Declaration* defStructA = top->localDeclarations().first();
//
//     QCOMPARE(top->localDeclarations()[1]->abstractType()->indexed(), top->localDeclarations()[0]->abstractType()->indexed());
//
//     QCOMPARE(idType->declaration(top), defStructA);
//     QVERIFY(defStructA->type<CppClassType>());
//     QVERIFY(defStructA->internalContext());
//     QCOMPARE(defStructA->internalContext()->localDeclarations().count(), 1);
//     Cpp::ClassDeclaration* classDecl = dynamic_cast<Cpp::ClassDeclaration*>(top->localDeclarations()[0]);
//     QVERIFY(classDecl);
//
//     QCOMPARE(classDecl->classType(), Cpp::ClassDeclarationData::Struct);
//
//   }
    //                 0         1         2         3         4         5         6         7
    //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
  {
    QByteArray method("class A { public: union { struct { int x; }; int p; }; struct { int b; }; struct { int c; } cc; }; A test; void test() { test.x = 1; test.b = 1; test.p = 1; test.cc.c = 1; }");

    LockedTopDUContext top = parse(method, DumpNone);

    QCOMPARE(top->childContexts().count(), 3);
    QCOMPARE(top->childContexts()[0]->childContexts().count(), 3);
    QCOMPARE(top->childContexts()[0]->childContexts()[2]->localDeclarations().count(), 1);
    QVERIFY(!top->childContexts()[0]->childContexts()[2]->localDeclarations()[0]->uses().isEmpty());
    QCOMPARE(top->childContexts()[0]->childContexts()[1]->localDeclarations().count(), 1);
    QVERIFY(top->childContexts()[0]->childContexts()[1]->isPropagateDeclarations());
    QVERIFY(top->childContexts()[0]->childContexts()[0]->isPropagateDeclarations());
    QVERIFY(top->childContexts()[0]->childContexts()[0]->inSymbolTable());
    QVERIFY(top->childContexts()[0]->childContexts()[1]->inSymbolTable());
    QVERIFY(top->childContexts()[0]->childContexts()[0]->localScopeIdentifier().isEmpty());
    QVERIFY(top->childContexts()[0]->childContexts()[1]->localScopeIdentifier().isEmpty());
    QVERIFY(!top->childContexts()[0]->childContexts()[1]->localDeclarations()[0]->uses().isEmpty());
    QCOMPARE(top->childContexts()[0]->childContexts()[0]->childContexts().count(), 1);
    QCOMPARE(top->childContexts()[0]->childContexts()[0]->childContexts()[0]->localDeclarations().count(), 1);
    QVERIFY(top->childContexts()[0]->childContexts()[0]->isPropagateDeclarations());
    QVERIFY(top->childContexts()[0]->childContexts()[0]->childContexts()[0]->isPropagateDeclarations());
    QVERIFY(!top->childContexts()[0]->childContexts()[0]->childContexts()[0]->localDeclarations()[0]->uses().isEmpty());
    QCOMPARE(top->childContexts()[2]->usesCount(), 9);

  }

  {
    //                 0         1         2         3         4         5         6         7
    //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
    QByteArray method("typedef struct { short i; } A; A instance;");

    LockedTopDUContext top = parse(method, DumpNone);

    QVERIFY(!top->parentContext());
    QCOMPARE(top->childContexts().count(), 1);
    QCOMPARE(top->localDeclarations().count(), 3);
    QVERIFY(top->localScopeIdentifier().isEmpty());

    AbstractType::Ptr t = top->localDeclarations()[0]->abstractType();
    IdentifiedType* idType = dynamic_cast<IdentifiedType*>(t.unsafeData());
    QVERIFY(idType);
    QVERIFY(idType->qualifiedIdentifier().count() == 1);
    QVERIFY(idType->qualifiedIdentifier().at(0).uniqueToken());

    QVERIFY(top->localDeclarations()[1]->isTypeAlias());
    QCOMPARE(top->localDeclarations()[1]->identifier(), Identifier("A"));
    QCOMPARE(top->localDeclarations()[2]->abstractType()->indexed(), top->localDeclarations()[1]->abstractType()->indexed());

    QCOMPARE(top->localDeclarations()[1]->uses().count(), 1);
    QCOMPARE(top->localDeclarations()[1]->uses().begin()->count(), 1);

    Declaration* defStructA = top->localDeclarations().first();
    QVERIFY(!defStructA->isTypeAlias());
    QVERIFY(defStructA->identifier().uniqueToken());

    QCOMPARE(idType->declaration(top), defStructA);
    QVERIFY(defStructA->type<CppClassType>());
    QVERIFY(defStructA->internalContext());
    QCOMPARE(defStructA->internalContext()->localDeclarations().count(), 1);
    ClassDeclaration* classDecl = dynamic_cast<ClassDeclaration*>(defStructA);
    QVERIFY(classDecl);
    QCOMPARE(classDecl->classType(), ClassDeclarationData::Struct);

    QVERIFY(!findDeclaration(top, Identifier("i")));
    QVERIFY(findDeclaration(top, QualifiedIdentifier("A::i")));

  }
  {
    //                 0         1         2         3         4         5         6         7
    //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
    QByteArray method("typedef struct B A; A instance;");

    LockedTopDUContext top = parse(method, DumpNone);

    QVERIFY(!top->parentContext());
    QCOMPARE(top->localDeclarations().count(), 3);
    QVERIFY(top->localScopeIdentifier().isEmpty());

    AbstractType::Ptr t = top->localDeclarations()[0]->abstractType();
    IdentifiedType* idType = dynamic_cast<IdentifiedType*>(t.unsafeData());
    QVERIFY(idType);
    QVERIFY(idType->qualifiedIdentifier().count() == 1);

    QVERIFY(top->localDeclarations()[1]->isTypeAlias());
    QCOMPARE(top->localDeclarations()[1]->identifier(), Identifier("A"));
    QCOMPARE(top->localDeclarations()[2]->abstractType()->indexed(), top->localDeclarations()[1]->abstractType()->indexed());

    QCOMPARE(top->localDeclarations()[1]->uses().count(), 1);
    QCOMPARE(top->localDeclarations()[1]->uses().begin()->count(), 1);

    Declaration* defStructA = top->localDeclarations().first();
    QVERIFY(!defStructA->isTypeAlias());
    QVERIFY(defStructA->isForwardDeclaration());

  }
  {
    //                 0         1         2         3         4         5         6         7
    //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
    QByteArray method("struct A { short i; A(int b, int c) : i(c) { } virtual void test(int j) = 0; }; A instance;");

    LockedTopDUContext top = parse(method, DumpNone);

    QVERIFY(!top->parentContext());
    QCOMPARE(top->childContexts().count(), 1);
    QCOMPARE(top->localDeclarations().count(), 2);
    QVERIFY(top->localScopeIdentifier().isEmpty());

    AbstractType::Ptr t = top->localDeclarations()[1]->abstractType();
    IdentifiedType* idType = dynamic_cast<IdentifiedType*>(t.unsafeData());
    QVERIFY(idType);
    QCOMPARE( idType->qualifiedIdentifier(), QualifiedIdentifier("A") );

    Declaration* defStructA = top->localDeclarations().first();
    QCOMPARE(defStructA->identifier(), Identifier("A"));
    QCOMPARE(defStructA->uses().count(), 1);
    QCOMPARE(defStructA->uses().begin()->count(), 1);
    QVERIFY(defStructA->type<CppClassType>());
    ClassDeclaration* classDecl = dynamic_cast<ClassDeclaration*>(defStructA);
    QVERIFY(classDecl);
    QCOMPARE(classDecl->classType(), ClassDeclarationData::Struct);

    DUContext* structA = top->childContexts().first();
    QVERIFY(structA->parentContext());
    QCOMPARE(structA->importedParentContexts().count(), 0);
    QCOMPARE(structA->childContexts().count(), 3);
    QCOMPARE(structA->localDeclarations().count(), 3);
    QCOMPARE(structA->localScopeIdentifier(), QualifiedIdentifier("A"));

    Declaration* defI = structA->localDeclarations().first();
    QCOMPARE(defI->identifier(), Identifier("i"));
    QCOMPARE(defI->uses().count(), 1);
    QCOMPARE(defI->uses().begin()->count(), 1);

    QCOMPARE(findDeclaration(structA,  Identifier("i")), defI);
    QCOMPARE(findDeclaration(structA,  Identifier("b")), noDef);
    QCOMPARE(findDeclaration(structA,  Identifier("c")), noDef);

    DUContext* ctorImplCtx = structA->childContexts()[1];
    QVERIFY(ctorImplCtx->parentContext());
    QCOMPARE(ctorImplCtx->importedParentContexts().count(), 1);
    QCOMPARE(ctorImplCtx->childContexts().count(), 1);
    QCOMPARE(ctorImplCtx->localDeclarations().count(), 0);
    QVERIFY(!ctorImplCtx->localScopeIdentifier().isEmpty());
    QVERIFY(ctorImplCtx->owner());

    DUContext* ctorCtx = ctorImplCtx->importedParentContexts().first().context(0);
    QVERIFY(ctorCtx->parentContext());
    QCOMPARE(ctorCtx->childContexts().count(), 0);
    QCOMPARE(ctorCtx->localDeclarations().count(), 2);
    QCOMPARE(ctorCtx->localScopeIdentifier(), QualifiedIdentifier("A")); ///@todo check if it should really be this way

    Declaration* defB = ctorCtx->localDeclarations().first();
    QCOMPARE(defB->identifier(), Identifier("b"));
    QCOMPARE(defB->uses().count(), 0);

    Declaration* defC = ctorCtx->localDeclarations()[1];
    QCOMPARE(defC->identifier(), Identifier("c"));
    QCOMPARE(defC->uses().count(), 1);
    QCOMPARE(defC->uses().begin()->count(), 1);

    Declaration* defTest = structA->localDeclarations()[2];
    QCOMPARE(defTest->identifier(), Identifier("test"));
    ClassFunctionDeclaration* classFunDecl = dynamic_cast<ClassFunctionDeclaration*>(defTest);
    QVERIFY(classFunDecl);
    QVERIFY(classFunDecl->isAbstract());

    QCOMPARE(findDeclaration(ctorCtx,  Identifier("i")), defI);
    QCOMPARE(findDeclaration(ctorCtx,  Identifier("b")), defB);
    QCOMPARE(findDeclaration(ctorCtx,  Identifier("c")), defC);

    DUContext* testCtx = structA->childContexts().last();
    QCOMPARE(testCtx->childContexts().count(), 0);
    QCOMPARE(testCtx->localDeclarations().count(), 1);
    QCOMPARE(testCtx->localScopeIdentifier(), QualifiedIdentifier("test")); ///@todo check if it should really be this way

    Declaration* defJ = testCtx->localDeclarations().first();
    QCOMPARE(defJ->identifier(), Identifier("j"));
    QCOMPARE(defJ->uses().count(), 0);

    /*DUContext* insideCtorCtx = ctorCtx->childContexts().first();
    QCOMPARE(insideCtorCtx->childContexts().count(), 0);
    QCOMPARE(insideCtorCtx->localDeclarations().count(), 0);
    QVERIFY(insideCtorCtx->localScopeIdentifier().isEmpty());*/

  }
  {
    //                 0         1         2         3         4         5         6         7
    //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
    QByteArray method("struct A { short i; }; struct A instance; void test(struct A * a) { a->i = instance.i; };");

    LockedTopDUContext top = parse(method, DumpNone);

    QVERIFY(!top->parentContext());
    QCOMPARE(top->childContexts().count(), 3);
    QCOMPARE(top->localDeclarations().count(), 3);
    QVERIFY(top->localScopeIdentifier().isEmpty());

    Declaration* defStructA = top->localDeclarations().first();
    QCOMPARE(defStructA->identifier(), Identifier("A"));
    QCOMPARE(defStructA->uses().count(), 1);
    QCOMPARE(defStructA->uses().begin()->count(), 2);
    QVERIFY(defStructA->type<CppClassType>());
    ClassDeclaration* classDecl = dynamic_cast<ClassDeclaration*>(defStructA);
    QVERIFY(classDecl);
    QCOMPARE(classDecl->classType(), ClassDeclarationData::Struct);

    DUContext* structA = top->childContexts().first();
    QCOMPARE(structA->parentContext(), top.m_top);
    QCOMPARE(structA->importedParentContexts().count(), 0);
    QCOMPARE(structA->childContexts().count(), 0);
    QCOMPARE(structA->localDeclarations().count(), 1);
    QCOMPARE(structA->localScopeIdentifier(), QualifiedIdentifier("A"));

    Declaration* defI = structA->localDeclarations().first();
    QCOMPARE(defI->identifier(), Identifier("i"));
    QCOMPARE(defI->uses().count(), 1);
    QCOMPARE(defI->uses().begin()->count(), 2);

    QCOMPARE(findDeclaration(structA,  Identifier("i")), defI);

  }
}

void TestDUChain::testDeclareStructInNamespace()
{
  TEST_FILE_PARSE_ONLY

  {
    QByteArray method("namespace A { class B { class C; } ; } namespace A { class A::B::C { }; }");

    LockedTopDUContext top = parse(method, DumpAll);

    QCOMPARE(top->childContexts().count(), 2);
    QCOMPARE(top->childContexts()[0]->childContexts().size(), 1);
    QCOMPARE(top->childContexts()[0]->childContexts()[0]->localDeclarations().size(), 1);

    ForwardDeclaration* forwardDecl = dynamic_cast<ForwardDeclaration*>(top->childContexts()[0]->childContexts()[0]->localDeclarations()[0]);
    QVERIFY(forwardDecl);
    QVERIFY(forwardDecl->resolve(top));

  }

  {
    //                 0         1         2         3         4         5         6         7
    //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
    QByteArray method("struct A {A(); struct B;}; struct A::B { B(); struct C; }; struct A::B::C { A mem; B mem2; void test(A param); }; void A::B::C::test(A param) {};");

    LockedTopDUContext top = parse(method, DumpNone);

    QVERIFY(!top->parentContext());
    QCOMPARE(top->childContexts().count(), 5);
    QCOMPARE(top->localDeclarations().count(), 2); //Only one declaration, because the others are nested within helper scope contexts

    QCOMPARE(top->childContexts()[1]->localDeclarations().count(), 1);
    QCOMPARE(top->childContexts()[2]->localDeclarations().count(), 1);
    QCOMPARE(top->childContexts()[3]->localDeclarations().count(), 1);

    QCOMPARE(top->childContexts()[2]->localScopeIdentifier(), QualifiedIdentifier("A::B"));
    QCOMPARE(top->childContexts()[2]->childContexts()[0]->localScopeIdentifier(), QualifiedIdentifier("C"));
    QCOMPARE(top->childContexts()[2]->childContexts()[0]->scopeIdentifier(true), QualifiedIdentifier("A::B::C"));
    QCOMPARE(top->childContexts()[2]->localDeclarations()[0]->qualifiedIdentifier(), QualifiedIdentifier("A::B::C"));
    QVERIFY(top->childContexts()[2]->inSymbolTable());
    QVERIFY(top->childContexts()[2]->childContexts()[0]->inSymbolTable());
    QVERIFY(top->childContexts()[2]->localDeclarations()[0]->inSymbolTable());

    QualifiedIdentifier search("::A::B::C");
    Declaration* cDecl = findDeclaration(top, search);
    QVERIFY(cDecl);
    QVERIFY(cDecl->logicalInternalContext(top));
    QVERIFY(cDecl);
    QVERIFY(!cDecl->isForwardDeclaration());
    QVERIFY(cDecl->internalContext());

    QCOMPARE(top->childContexts()[2]->childContexts().count(), 1);
    QCOMPARE(top->childContexts()[2]->childContexts()[0]->localDeclarations().count(), 3);
    kDebug() << top->childContexts()[2]->childContexts()[0]->localDeclarations()[0]->abstractType()->toString();
    kDebug() << top->localDeclarations()[0]->abstractType()->toString();
    QCOMPARE(top->childContexts()[2]->childContexts()[0]->localDeclarations()[0]->indexedType(), top->localDeclarations()[0]->indexedType());
    QCOMPARE(top->childContexts()[2]->childContexts()[0]->localDeclarations()[1]->indexedType(), top->childContexts()[1]->localDeclarations()[0]->indexedType());
    kDebug() << top->childContexts()[3]->localDeclarations()[0]->abstractType()->toString();
    kDebug() << top->localDeclarations()[0]->abstractType()->toString();
    QCOMPARE(top->childContexts()[3]->localDeclarations()[0]->indexedType(), top->localDeclarations()[0]->indexedType());

  }
  //                 0         1         2         3         4         5         6         7
  //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
  {
    QByteArray method("namespace B {class C {struct A;void test();};};using namespace B;struct C::A {};");

    LockedTopDUContext top = parse(method, DumpNone);

    QVERIFY(!top->parentContext());
    QCOMPARE(top->childContexts().count(), 2);
    QCOMPARE(top->localDeclarations().count(), 2);
    QCOMPARE(top->childContexts()[0]->localDeclarations().count(), 1);
    QCOMPARE(top->childContexts()[0]->childContexts().count(), 1);
    QCOMPARE(top->childContexts()[0]->childContexts()[0]->localDeclarations().count(), 2);
    QVERIFY(top->childContexts()[0]->childContexts()[0]->localDeclarations()[0]->isForwardDeclaration());
    KDevelop::ForwardDeclaration* forward = dynamic_cast<KDevelop::ForwardDeclaration*>(top->childContexts()[0]->childContexts()[0]->localDeclarations()[0]);
    QVERIFY(forward);
    QVERIFY(forward->resolve(top));

  }
}
void TestDUChain::testCStruct()
{
  TEST_FILE_PARSE_ONLY

  //                 0         1         2         3         4         5         6         7
  //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
  QByteArray method("struct A {int i; }; struct A instance; typedef struct { int a; } B, *BPointer;");

  LockedTopDUContext top = parse(method, DumpNone);

  QVERIFY(!top->parentContext());
  QCOMPARE(top->childContexts().count(), 2);
  QCOMPARE(top->localDeclarations().count(), 5);

  QVERIFY(top->localDeclarations()[0]->kind() == Declaration::Type);
  QVERIFY(top->localDeclarations()[1]->kind() == Declaration::Instance);

}

void TestDUChain::testCStruct2()
{
  TEST_FILE_PARSE_ONLY

  //                 0         1         2         3         4         5         6         7
  //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
  {
  QByteArray method("struct A {struct C c; }; struct C { int v; };");

  //Expected result: the elaborated type-specifier "struct C" within the declaration "struct A" should create a new global declaration.


  LockedTopDUContext top = parse(method, DumpNone);

  QVERIFY(!top->parentContext());
  QCOMPARE(top->childContexts().count(), 2);
  QCOMPARE(top->localDeclarations().count(), 3); //3 declarations because the elaborated type-specifier "struct C" creates a forward-declaration on the global scope
  QCOMPARE(top->childContexts()[0]->localDeclarations().count(), 1);
  kDebug() << "TYPE:" << top->childContexts()[0]->localDeclarations()[0]->abstractType()->toString() << typeid(*top->childContexts()[0]->localDeclarations()[0]->abstractType()).name();
  QCOMPARE(top->childContexts()[0]->localDeclarations()[0]->indexedType(), top->localDeclarations()[1]->indexedType());

  QCOMPARE(top->childContexts()[0]->localDeclarations()[0]->kind(), Declaration::Instance);
  //QVERIFY(top->localDeclarations()[0]->kind() == Declaration::Type);
  //QVERIFY(top->localDeclarations()[1]->kind() == Declaration::Instance);
  }
  {
  QByteArray method("struct A {inline struct C c() {}; }; struct C { int v; };");

  //Expected result: the elaborated type-specifier "struct C" within the declaration "struct A" should create a new global declaration.


  LockedTopDUContext top = parse(method, DumpNone);

  QVERIFY(!top->parentContext());
  QCOMPARE(top->childContexts().count(), 2);
  QCOMPARE(top->localDeclarations().count(), 3); //3 declarations because the elaborated type-specifier "struct C" creates a forward-declaration on the global scope
  QCOMPARE(top->childContexts()[0]->localDeclarations().count(), 1);
  kDebug() << "TYPE:" << top->childContexts()[0]->localDeclarations()[0]->abstractType()->toString() << typeid(*top->childContexts()[0]->localDeclarations()[0]->abstractType()).name();
  FunctionType::Ptr function(top->childContexts()[0]->localDeclarations()[0]->type<FunctionType>());
  QVERIFY(function);
  kDebug() << "RETURN:" << function->returnType()->toString() << typeid(*function->returnType()).name();
  QCOMPARE(function->returnType()->indexed(), top->localDeclarations()[1]->indexedType());
  //QCOMPARE(top->childContexts()[0]->localDeclarations()[0]->indexedType(), top->localDeclarations()[1]->indexedType());

  //QCOMPARE(top->childContexts()[0]->localDeclarations()[0]->kind(), Declaration::Instance);
  //QVERIFY(top->localDeclarations()[0]->kind() == Declaration::Type);
  //QVERIFY(top->localDeclarations()[1]->kind() == Declaration::Instance);
  }

}

void TestDUChain::testVariableDeclaration()
{
  TEST_FILE_PARSE_ONLY

  //                 0         1         2         3         4         5         6         7
  //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
    QByteArray method("struct A{}; int c, q;\n"
                      "A instance1(c); A instance2(2, 3); A instance3(q);\n"
                      "void bla() {int* i = new A(c); }\n");

  LockedTopDUContext top = parse(method, DumpAll);

  QVERIFY(!top->parentContext());
  QVERIFY(top->localScopeIdentifier().isEmpty());
  QCOMPARE(top->childContexts().count(), 3);
  QCOMPARE(top->localDeclarations().count(), 7);

  // struct A
  Declaration* defStructA = top->localDeclarations().first();
  QCOMPARE(defStructA->identifier(), Identifier("A"));

  // int c;
  Declaration* defIntC = top->localDeclarations().at(1);
  QCOMPARE(defIntC->uses().count(), 1);
  QCOMPARE(defIntC->uses().begin()->count(), 2);

  // instances
  for(int i = 3; i < 6; ++i) {
    Declaration* inst = top->localDeclarations().at(i);
    QVERIFY(!inst->isFunctionDeclaration());
    StructureType::Ptr idType = inst->abstractType().cast<StructureType>();
    QVERIFY(idType);
    QCOMPARE( idType->qualifiedIdentifier(), QualifiedIdentifier("A") );
  }

  // bla
  QVERIFY(top->localDeclarations().at(6)->isFunctionDeclaration());
}

void TestDUChain::testDeclareClass()
{
  TEST_FILE_PARSE_ONLY

  //                 0         1         2         3         4         5         6         7
  //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
  QByteArray method("class A { A() {}; A rec; void test(int); }; void A::test(int j) {}");

  LockedTopDUContext top = parse(method, DumpNone);

  DumpDotGraph dump;
//  kDebug() << "dot-graph: \n" << dump.dotGraph(top, false);

//  kDebug() << "ENDE ENDE ENDE";
//  kDebug() << "dot-graph: \n" << dump.dotGraph(top, false);

  QVERIFY(!top->parentContext());
  QCOMPARE(top->childContexts().count(), 3);
  QCOMPARE(top->localDeclarations().count(), 2);
  QVERIFY(top->localScopeIdentifier().isEmpty());

  Declaration* defClassA = top->localDeclarations().first();
  QCOMPARE(defClassA->identifier(), Identifier("A"));
  QCOMPARE(defClassA->uses().count(), 1);
  QCOMPARE(defClassA->uses().begin()->count(), 2);
  QVERIFY(defClassA->type<CppClassType>());
  QVERIFY(defClassA->internalContext());
  QCOMPARE(defClassA->internalContext()->range().start.column, 8); //The class-context should start directly behind the name
//   QVERIFY(function);
//   QCOMPARE(function->returnType(), typeVoid);
//   QCOMPARE(function->arguments().count(), 1);
//   QCOMPARE(function->arguments().first(), typeInt);

  QVERIFY(defClassA->internalContext());

  QCOMPARE(defClassA->internalContext()->localDeclarations().count(), 3);
  QCOMPARE(defClassA->internalContext()->localDeclarations()[1]->abstractType()->indexed(), defClassA->abstractType()->indexed());

  DUContext* classA = top->childContexts().first();
  QVERIFY(classA->parentContext());
  QCOMPARE(classA->importedParentContexts().count(), 0);
  QCOMPARE(classA->childContexts().count(), 3);
  QCOMPARE(classA->localDeclarations().count(), 3);
  QCOMPARE(classA->localScopeIdentifier(), QualifiedIdentifier("A"));

  Declaration* defRec = classA->localDeclarations()[1];
  QVERIFY(defRec->abstractType());
  QCOMPARE(defRec->abstractType()->indexed(), defClassA->abstractType()->indexed());

}

void TestDUChain::testDeclareFriend()
{
  TEST_FILE_PARSE_ONLY

  //                 0         1         2         3         4         5         6         7
  //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
  QByteArray method("class B {}; class A { friend class F; friend class B; }; ");

  LockedTopDUContext top = parse(method, DumpNone);

  DumpDotGraph dump;
//  kDebug() << "dot-graph: \n" << dump.dotGraph(top, false);

//  kDebug() << "ENDE ENDE ENDE";
//  kDebug() << "dot-graph: \n" << dump.dotGraph(top, false);

  QVERIFY(!top->parentContext());
  QCOMPARE(top->childContexts().count(), 2);
  QCOMPARE(top->localDeclarations().count(), 3); //1 Forward-declaration of F
  QVERIFY(top->localScopeIdentifier().isEmpty());

  Declaration* defClassA = top->localDeclarations()[1];
  QCOMPARE(defClassA->identifier(), Identifier("A"));
  QCOMPARE(defClassA->uses().count(), 0);
  QVERIFY(defClassA->type<CppClassType>());
  QVERIFY(defClassA->internalContext());

  QCOMPARE(top->localDeclarations()[2]->identifier(), Identifier("F"));

  QCOMPARE(top->childContexts()[1]->localDeclarations().count(), 2); //friend-declaration
  QCOMPARE(top->childContexts()[1]->localDeclarations()[0]->identifier(), Identifier("friend"));
  QCOMPARE(top->childContexts()[1]->localDeclarations()[1]->identifier(), Identifier("friend"));

  QVERIFY(Cpp::isFriend(top->localDeclarations()[1], top->localDeclarations()[2]));
  QVERIFY(Cpp::isFriend(top->localDeclarations()[1], top->localDeclarations()[0]));

  QVERIFY(top->localDeclarations()[0]->uses().size());
  QVERIFY(top->localDeclarations()[0]->uses().begin()->size());

}

void TestDUChain::testDeclareNamespace()
{
  TEST_FILE_PARSE_ONLY

  //                 0         1         2         3         4         5         6         7
  //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
  QByteArray method("namespace foo { int bar; } int bar; int test() { return foo::bar; }");

  LockedTopDUContext top = parse(method, DumpNone);


  QVERIFY(!top->parentContext());
  QCOMPARE(top->childContexts().count(), 3);
  QCOMPARE(top->localDeclarations().count(), 3);
  QVERIFY(top->localScopeIdentifier().isEmpty());
  QCOMPARE(findDeclaration(top, Identifier("foo")), top->localDeclarations()[0]);
  QCOMPARE(top->childContexts()[0]->range(), RangeInRevision(0, 14, 0, 26));
  QVERIFY(top->localDeclarations()[0]->inSymbolTable());

  QVERIFY(top->localDeclarations()[0]->inSymbolTable());
  QVERIFY(top->localDeclarations()[0]->uses().size());

  DUContext* fooCtx = top->childContexts().first();
  QVERIFY(fooCtx->inSymbolTable());
  QCOMPARE(fooCtx->childContexts().count(), 0);
  QCOMPARE(fooCtx->localDeclarations().count(), 1);
  QCOMPARE(fooCtx->localScopeIdentifier(), QualifiedIdentifier("foo"));
  QCOMPARE(fooCtx->scopeIdentifier(), QualifiedIdentifier("foo"));

  DUContext* testCtx = top->childContexts()[2];
  QCOMPARE(testCtx->childContexts().count(), 0);
  QCOMPARE(testCtx->localDeclarations().count(), 0);
  QCOMPARE(testCtx->localScopeIdentifier(), QualifiedIdentifier("test"));
  QCOMPARE(testCtx->scopeIdentifier(), QualifiedIdentifier("test"));

  Declaration* bar2 = top->localDeclarations()[1];
  QCOMPARE(bar2->identifier(), Identifier("bar"));
  QCOMPARE(bar2->qualifiedIdentifier(), QualifiedIdentifier("bar"));
  QCOMPARE(bar2->uses().count(), 0);

  Declaration* bar = fooCtx->localDeclarations().first();
  QCOMPARE(bar->identifier(), Identifier("bar"));
  QCOMPARE(bar->qualifiedIdentifier(), QualifiedIdentifier("foo::bar"));
  QCOMPARE(findDeclaration(testCtx,  QualifiedIdentifier("foo::bar")), bar);
  QCOMPARE(bar->uses().count(), 1);
  QCOMPARE(bar->uses().begin()->count(), 1);
  QCOMPARE(findDeclaration(top, bar->identifier()), bar2);
  QCOMPARE(findDeclaration(top, bar->qualifiedIdentifier()), bar);

  QCOMPARE(findDeclaration(top, QualifiedIdentifier("bar")), bar2);
  QCOMPARE(findDeclaration(top, QualifiedIdentifier("::bar")), bar2);
  QCOMPARE(findDeclaration(top, QualifiedIdentifier("foo::bar")), bar);
  QCOMPARE(findDeclaration(top, QualifiedIdentifier("::foo::bar")), bar);

  IndexedQualifiedIdentifier fooId(QualifiedIdentifier("foo"));

  uint itemCount;
  const KDevelop::CodeModelItem* items;
  CodeModel::self().items(top->url(), itemCount, items);
  for(uint a = 0; a < itemCount; ++a) {
    if(items[a].id == fooId) {
      QCOMPARE(items[a].referenceCount, 1u); //Once by the namespace, once by the declaration
    }
  }

}

void TestDUChain::testDeclareNamespace2()
{
  TEST_FILE_PARSE_ONLY

  //                 0         1         2         3         4         5         6         7
  //                 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012
  QByteArray method("struct A {}; namespace B { struct A : ::A {}; }");

  LockedTopDUContext top = parse(method, DumpNone);

  QVERIFY(!top->parentContext());
  QCOMPARE(top->localDeclarations().count(), 2);
  QCOMPARE(top->childContexts().count(), 2);
  QCOMPARE(top->childContexts()[1]->localDeclarations().count(), 1);

  CppClassType::Ptr outerA = top->localDeclarations()[0]->abstractType().cast<CppClassType>();
  CppClassType::Ptr innerA = top->childContexts()[1]->localDeclarations()[0]->abstractType().cast<CppClassType>();

  QVERIFY(outerA);
  QVERIFY(innerA);

  Declaration* klassDecl = innerA->declaration(top);
  ClassDeclaration* cppClassDecl = dynamic_cast<ClassDeclaration*>(klassDecl);

  QVERIFY(cppClassDecl);
  QCOMPARE(cppClassDecl->baseClassesSize(), (uint)1);
  QCOMPARE(cppClassDecl->baseClasses()->baseClass, outerA->indexed());

}

void TestDUChain::testSearchAcrossNamespace()
{
  TEST_FILE_PARSE_ONLY

  //                 0         1         2         3         4         5         6         7
  //                 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012
  QByteArray method("namespace A { class B{}; } class B{}; namespace A{ B bla; }");

  LockedTopDUContext top = parse(method, DumpNone);

  QVERIFY(!top->parentContext());
  QCOMPARE(top->childContexts().count(), 3);
  QCOMPARE(top->childContexts()[0]->localDeclarations().count(), 1);
  QCOMPARE(top->childContexts()[2]->localDeclarations().count(), 1);
  QVERIFY(top->childContexts()[2]->localDeclarations()[0]->abstractType());
  QCOMPARE(top->childContexts()[2]->localDeclarations()[0]->abstractType()->indexed(), top->childContexts()[0]->localDeclarations()[0]->abstractType()->indexed());
  QVERIFY(!top->childContexts()[2]->localDeclarations()[0]->abstractType().cast<DelayedType>());

}

void TestDUChain::testSearchAcrossNamespace2()
{
  TEST_FILE_PARSE_ONLY

  //                 0         1         2         3         4         5         6         7
  //                 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012
  QByteArray method("namespace A {class B{}; } namespace A { class C{ void member(B);};  } void A::C::member(B b) {B c;}");

  LockedTopDUContext top = parse(method, DumpNone);

  QVERIFY(!top->parentContext());
  QCOMPARE(top->childContexts().count(), 4);
  QCOMPARE(top->childContexts()[0]->localDeclarations().count(), 1);
  QCOMPARE(top->childContexts()[1]->localDeclarations().count(), 1);
  QCOMPARE(top->childContexts()[2]->localDeclarations().count(), 1);
  QCOMPARE(top->childContexts()[2]->type(), DUContext::Function);
  QCOMPARE(top->childContexts()[2]->localDeclarations().count(), 1);
  QCOMPARE(top->childContexts()[2]->localScopeIdentifier(), QualifiedIdentifier("A::C::member"));
  QVERIFY(top->childContexts()[2]->localDeclarations()[0]->abstractType()); //B should be found from that position
  QVERIFY(!top->childContexts()[2]->localDeclarations()[0]->abstractType().cast<DelayedType>());
  QCOMPARE(top->childContexts()[3]->type(), DUContext::Other);
  QCOMPARE(top->childContexts()[3]->localDeclarations().count(), 1);
  QVERIFY(top->childContexts()[3]->localDeclarations()[0]->abstractType()); //B should be found from that position
  QCOMPARE(top->childContexts()[3]->localScopeIdentifier(), QualifiedIdentifier("A::C::member"));
  QVERIFY(!top->childContexts()[3]->localDeclarations()[0]->abstractType().cast<DelayedType>());

}

void TestDUChain::testSearchAcrossNamespace3()
{
  TEST_FILE_PARSE_ONLY

  //                 0         1         2         3         4         5         6         7
  //                 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012
  QByteArray method("namespace B { class C { }; } namespace A { class C; } using namespace B; namespace A { C c; };");

  LockedTopDUContext top = parse(method, DumpNone);

  QCOMPARE(top->childContexts().count(), 3);
  QCOMPARE(top->localDeclarations().count(), 4);
  QCOMPARE(top->childContexts()[2]->localDeclarations().count(), 1);
  QCOMPARE(top->childContexts()[2]->localDeclarations()[0]->abstractType()->toString(), QString("A::C"));
}

void TestDUChain::testADL()
{
  {
    QByteArray nonAdlCall("namespace foo { struct A {}; int bar(A& a) {} }"
                          "struct A {};"
                          "int bar(int& a) {}"
                          "int test() { A a; bar(a); }"); // calls ::bar

    LockedTopDUContext top( parse(nonAdlCall, DumpNone) );

    QCOMPARE(top->childContexts().count(), 6);

    // foo::bar is never used
    QCOMPARE(top->childContexts()[0]->localDeclarations().size(), 2);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->qualifiedIdentifier().toString(), QString("foo::bar"));
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().size(), 0);

    // ::bar has 1 use
    QCOMPARE(top->localDeclarations().size(), 4);
    QCOMPARE(top->localDeclarations()[2]->qualifiedIdentifier().toString(), QString("bar"));
    QCOMPARE(top->localDeclarations()[2]->uses().size(), 1);
    QCOMPARE(top->localDeclarations()[2]->uses().begin()->size(), 1);
  }
  {
    QByteArray nonAdlCall("namespace foo { struct A {}; int bar(A& a) {} }"
                          "int bar(foo::A& a) {}" // found on normal lookup, hiding foo::bar
                          "int test() { A a; bar(a); }"); // calls ::bar

    LockedTopDUContext top( parse(nonAdlCall, DumpNone) );

    QCOMPARE(top->childContexts().count(), 5);

    // foo::bar is hidden by successful normal name lookup
    QCOMPARE(top->childContexts()[0]->localDeclarations().size(), 2);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->qualifiedIdentifier().toString(), QString("foo::bar"));
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().size(), 0);

    // ::bar has 1 use
    QCOMPARE(top->localDeclarations().size(), 3);
    QCOMPARE(top->localDeclarations()[1]->qualifiedIdentifier().toString(), QString("bar"));
    QCOMPARE(top->localDeclarations()[1]->uses().size(), 1);
    QCOMPARE(top->localDeclarations()[1]->uses().begin()->size(), 1);
  }

  {
    QByteArray adlCall("namespace foo { struct A {}; int bar(A& a) {} }"
                       "struct A {};"
                       "int bar(int& a) {}"
                       "int test() { foo::A a; bar(a); }"); // calls foo::bar

    LockedTopDUContext top( parse(adlCall, DumpNone) );

    QCOMPARE(top->childContexts().count(), 6);

    // foo::bar has 1 use
    QCOMPARE(top->childContexts()[0]->localDeclarations().size(), 2);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->qualifiedIdentifier().toString(), QString("foo::bar"));
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().size(), 1);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().begin()->size(), 1);

    // ::bar is never used
    QCOMPARE(top->localDeclarations().size(), 4);
    QCOMPARE(top->localDeclarations()[2]->qualifiedIdentifier().toString(), QString("bar"));
    QCOMPARE(top->localDeclarations()[2]->uses().size(), 0);
  }

  {
    // check that the adl lookup is performed even if no other function with the same name
    // exists in the current namespace
    QByteArray adlCall("namespace foo { struct A {}; int bar(A& a) {} }"
                       "int test() { foo::A a; bar(a); }"); // calls foo::bar

    LockedTopDUContext top( parse(adlCall, DumpNone) );

    QCOMPARE(top->childContexts().count(), 3);

    // foo::bar has 1 use
    QCOMPARE(top->childContexts()[0]->localDeclarations().size(), 2);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->qualifiedIdentifier().toString(), QString("foo::bar"));
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().size(), 1);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().begin()->size(), 1);
  }
}

void TestDUChain::testADLConstness()
{
  // make sure a const-argument takes the const-overload while a non-const
  // argument takes the non-const overload
  // important e.g. for range-based for loops
  QByteArray code( "void foo(int&);\n"
                   "void foo(const int&);\n"
                   "void bar(const int&);\n"
                   "void bar(int&);\n"
                   "int test() {\n"
                   "  int i = 0; foo(i); bar(i);\n" // call to non-const
                   "  const int j = 0; foo(j); bar(j);\n" // call to const
                   "}\n");

  LockedTopDUContext top( parse(code, DumpNone) );
  QVERIFY(top->problems().isEmpty());

  QCOMPARE(top->localDeclarations().count(), 5);

  // foo: non-const version
  Declaration* dec = top->localDeclarations().at(0);
  FunctionType::Ptr func = dec->type<FunctionType>();
  QVERIFY(func);
  QCOMPARE(dec->uses().count(), 1);
  QCOMPARE(dec->uses().begin()->count(), 1);
  QCOMPARE(dec->uses().begin()->at(0).start.line, 5);

  // foo: const version
  dec = top->localDeclarations().at(1);
  func = dec->type<FunctionType>();
  QVERIFY(func);
  QCOMPARE(dec->uses().count(), 1);
  QCOMPARE(dec->uses().begin()->count(), 1);
  QCOMPARE(dec->uses().begin()->at(0).start.line, 6);

  // bar: const version
  dec = top->localDeclarations().at(2);
  func = dec->type<FunctionType>();
  QVERIFY(func);
  QCOMPARE(dec->uses().count(), 1);
  QCOMPARE(dec->uses().begin()->count(), 1);
  QCOMPARE(dec->uses().begin()->at(0).start.line, 6);

  // bar: non-const version
  dec = top->localDeclarations().at(3);
  func = dec->type<FunctionType>();
  QVERIFY(func);
  QCOMPARE(dec->uses().count(), 1);
  QCOMPARE(dec->uses().begin()->count(), 1);
  QCOMPARE(dec->uses().begin()->at(0).start.line, 5);
}

void TestDUChain::testADLConstness2()
{
  // make sure a const-argument takes the const-overload while a non-const
  // argument takes the non-const overload
  // important e.g. for range-based for loops
  QByteArray code( "struct l {};\n"
                   "void foo(l&);\n"
                   "void foo(const l&);\n"
                   "void bar(const l&);\n"
                   "void bar(l&);\n"
                   "int test() {\n"
                   "  l i = 0; foo(i); bar(i);\n" // call to non-const
                   "  const l j = 0; foo(j); bar(j);\n" // call to const
                   "}\n");

  LockedTopDUContext top( parse(code, DumpNone) );
  QVERIFY(top->problems().isEmpty());

  QCOMPARE(top->localDeclarations().count(), 6);

  // foo: non-const version
  Declaration* dec = top->localDeclarations().at(1);
  FunctionType::Ptr func = dec->type<FunctionType>();
  QVERIFY(func);
  QCOMPARE(dec->uses().count(), 1);
  QCOMPARE(dec->uses().begin()->count(), 1);
  QCOMPARE(dec->uses().begin()->at(0).start.line, 6);

  // foo: const version
  dec = top->localDeclarations().at(2);
  func = dec->type<FunctionType>();
  QVERIFY(func);
  QCOMPARE(dec->uses().count(), 1);
  QCOMPARE(dec->uses().begin()->count(), 1);
  QCOMPARE(dec->uses().begin()->at(0).start.line, 7);

  // bar: const version
  dec = top->localDeclarations().at(3);
  func = dec->type<FunctionType>();
  QVERIFY(func);
  QCOMPARE(dec->uses().count(), 1);
  QCOMPARE(dec->uses().begin()->count(), 1);
  QCOMPARE(dec->uses().begin()->at(0).start.line, 7);

  // bar: non-const version
  dec = top->localDeclarations().at(4);
  func = dec->type<FunctionType>();
  QVERIFY(func);
  QCOMPARE(dec->uses().count(), 1);
  QCOMPARE(dec->uses().begin()->count(), 1);
  QCOMPARE(dec->uses().begin()->at(0).start.line, 6);
}

void TestDUChain::testADLClassType()
{
  {
    // test lookup to base class namespace
    QByteArray adlCall("namespace foo { struct A {}; int bar(A& a) {} }"
                       "namespace boo { struct B : public foo::A {}; }"
                       "int test() { boo::B b; bar(b); }"); // calls foo::bar

    LockedTopDUContext top( parse(adlCall, DumpNone) );

    QCOMPARE(top->childContexts().count(), 4);

    // foo::bar has 1 use
    QCOMPARE(top->childContexts()[0]->localDeclarations().size(), 2);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->qualifiedIdentifier().toString(), QString("foo::bar"));
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().size(), 1);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().begin()->size(), 1);
  }

  {
    // test lookup to indirect base class namespace
    QByteArray adlCall("namespace foo { struct A {}; int bar(A& a) {} }"
                       "namespace boo { struct B : public foo::A {}; }"
                       "namespace zoo { struct C : public boo::B {}; }"
                       "int test() { zoo::C c; bar(c); }"); // calls foo::bar

    LockedTopDUContext top( parse(adlCall, DumpNone) );

    QCOMPARE(top->childContexts().count(), 5);

    // foo::bar has 1 use
    QCOMPARE(top->childContexts()[0]->localDeclarations().size(), 2);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->qualifiedIdentifier().toString(), QString("foo::bar"));
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().size(), 1);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().begin()->size(), 1);
  }

  {
    // test lookup for inner classes
    QByteArray adlCall("namespace foo { struct A { struct B {}; }; int bar(A::B&) {} }"
                       "int test() { foo::A::B b; bar(b); }"); // calls foo::bar

    LockedTopDUContext top( parse(adlCall, DumpNone) );

    QCOMPARE(top->childContexts().count(), 3);

    // foo::bar has 1 use
    QCOMPARE(top->childContexts()[0]->localDeclarations().size(), 2);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->qualifiedIdentifier().toString(), QString("foo::bar"));
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().size(), 1);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().begin()->size(), 1);
  }
}

void TestDUChain::testADLFunctionType()
{
  {
    QByteArray adlCall("namespace foo { struct A {}; int bar(void *a) {} }"
                       "foo::A f() {}"
                       "int test() { bar(&f); }"); // calls foo::bar through f's return type

    LockedTopDUContext top( parse(adlCall, DumpNone) );

    QCOMPARE(top->childContexts().count(), 5);

    // foo::bar has 1 use
    QCOMPARE(top->childContexts()[0]->localDeclarations().size(), 2);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->qualifiedIdentifier().toString(), QString("foo::bar"));
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().size(), 1);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().begin()->size(), 1);
  }

  {
    QByteArray adlCall("namespace foo { struct A {}; int bar(void *a) {} }"
                       "void f(foo::A) {}"
                       "int test() { bar(f); }"); // calls foo::bar through f's argument type

    LockedTopDUContext top( parse(adlCall, DumpNone) );

    QCOMPARE(top->childContexts().count(), 5);

    // foo::bar has 1 use
    QCOMPARE(top->childContexts()[0]->localDeclarations().size(), 2);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->qualifiedIdentifier().toString(), QString("foo::bar"));
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().size(), 1);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().begin()->size(), 1);
  }
}

void TestDUChain::testADLFunctionByName()
{
  {
    QByteArray adlCall("namespace foo { struct A {}; int bar(void *a) {} void f(int a) {}}"
                       "int test() { bar(&foo::f); }"); // calls foo::bar

    LockedTopDUContext top( parse(adlCall, DumpAll) );

    QCOMPARE(top->childContexts().count(), 3);

    // foo::bar has 1 use
    QCOMPARE(top->childContexts()[0]->localDeclarations().size(), 3);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->qualifiedIdentifier().toString(), QString("foo::bar"));
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().size(), 1);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().begin()->size(), 1);
  }
}

void TestDUChain::testADLEnumerationType()
{
  {
    QByteArray adlCall("namespace foo { enum A { enValue }; int bar(A a) {} }"
                       "int test() { foo::A a; bar(a); }"); // calls foo::bar

    LockedTopDUContext top( parse(adlCall, DumpNone) );

    QCOMPARE(top->childContexts().count(), 3);

    // foo::bar has 1 use
    QCOMPARE(top->childContexts()[0]->localDeclarations().size(), 2);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->qualifiedIdentifier().toString(), QString("foo::bar"));
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().size(), 1);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().begin()->size(), 1);
  }

  {
    QByteArray adlCall("namespace foo { enum A { enValue }; int bar(A a) {} }"
                       "int test() { bar(foo::enValue); }"); // calls foo::bar

    LockedTopDUContext top( parse(adlCall, DumpNone) );

    QCOMPARE(top->childContexts().count(), 3);

    // foo::bar has 1 use
    QCOMPARE(top->childContexts()[0]->localDeclarations().size(), 2);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->qualifiedIdentifier().toString(), QString("foo::bar"));
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().size(), 1);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().begin()->size(), 1);
  }
}

void TestDUChain::testADLClassMembers()
{
    QByteArray adlCall("namespace foo { struct A { int member; }; void bar(int A::*p) {} }"
                       "int test() { bar(&foo::A::member); }"); // calls foo::bar
    
    LockedTopDUContext top( parse(adlCall, DumpAll) );
    
    QCOMPARE(top->childContexts().count(), 3);
    
    // foo::bar has 1 use
    QCOMPARE(top->childContexts()[0]->localDeclarations().size(), 2);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->qualifiedIdentifier().toString(), QString("foo::bar"));

    QEXPECT_FAIL("", "ADL for class members doesn't work", Abort);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().size(), 1);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().begin()->size(), 1);
}

void TestDUChain::testADLMemberFunction()
{
    QByteArray adlCall("namespace foo { struct A { void mem_fun() {} }; void bar(void (A::*p)()) {} }"
    "int test() { void (foo::A::*p)() = &foo::A::mem_fun; bar(p); }"); // calls foo::bar (avoid testADLMemberFunctionByName)

    LockedTopDUContext top( parse(adlCall, DumpAll) );

    QCOMPARE(top->childContexts().count(), 3);

    // foo::bar has 1 use
    QCOMPARE(top->childContexts()[0]->localDeclarations().size(), 2);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->qualifiedIdentifier().toString(), QString("foo::bar"));

    QEXPECT_FAIL("", "ADL for class members doesn't work", Abort);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().size(), 1);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().begin()->size(), 1);
}

void TestDUChain::testADLMemberFunctionByName() 
{
  {
    QByteArray adlCall("namespace foo { struct A { void f(int a) {} }; int bar(void *a) {} }"
                       "int test() { bar(&foo::A::f); }"); // calls foo::bar

    LockedTopDUContext top( parse(adlCall, DumpAll) );

    QCOMPARE(top->childContexts().count(), 3);

    // foo::bar has 1 use
    QCOMPARE(top->childContexts()[0]->localDeclarations().size(), 2);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->qualifiedIdentifier().toString(), QString("foo::bar"));
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().size(), 1);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().begin()->size(), 1);
  }

  {
    // test lookup to base class namespace (rest of tests for associated classes are same as in testClassType)
    // Note: when using int bar(void *) or int bar(void (A::*p)(int)) the overload resolver
    // won't be able to do the conversion from void (boo::B::*)(int), so we use a template (just like in the real)
    // This setup also tests if the OverloadResolver can instantiate templates for pointers to member functions
    // At the current stage, the resolver fails for this.
    // Note 2: it seems also the syntax "void (T::*p)(int)" is not properly parsed at this time, so making this
    // work on this higher level is a dead end for now. For the template declaration below, the function declaration
    // is parsed as "int bar( function void (int) , void p )"

    // reported as bug 

    QByteArray adlCall("namespace foo { struct A {}; template<class T> int bar(void (T::*p)(int)) {} }"
                       "namespace boo { struct B : public foo::A { void f(int a) {} }; }"
                       "int test() { bar(&boo::B::f); }"); // calls foo::bar
    
    LockedTopDUContext top( parse(adlCall, DumpNone) );
    
    QCOMPARE(top->childContexts().count(), 4);
    
    // foo::bar has 1 use
    QCOMPARE(top->childContexts()[0]->localDeclarations().size(), 2);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->qualifiedIdentifier().toString(), QString("foo::bar"));

    QEXPECT_FAIL("", "ADL for class members doesn't work", Abort);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().size(), 1);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().begin()->size(), 1);
  }
}

void TestDUChain::testADLOperators()
{
  {
    QByteArray adlCall("namespace foo { struct A { }; A& operator+(const A&, int) {} }"
                       "int test() { foo::A a, b;  b = a + 1; }"); // calls foo::operator+
    
    LockedTopDUContext top( parse(adlCall, DumpAll) );
    
    QCOMPARE(top->childContexts().count(), 3);
    
    // foo::bar has 1 use
    QCOMPARE(top->childContexts()[0]->localDeclarations().size(), 2);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->qualifiedIdentifier().toString(), QString("foo::operator+"));

    // binary operator +
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().size(), 1);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().begin()->size(), 1);
  }

  {
    QByteArray adlCall("namespace foo { struct A { }; A& operator-(const A&) {} }"
                       "int test() { foo::A a, b;  b = -a; }"); // calls foo::operator-

    LockedTopDUContext top( parse(adlCall, DumpAll) );

    QCOMPARE(top->childContexts().count(), 3);

    // foo::bar has 1 use
    QCOMPARE(top->childContexts()[0]->localDeclarations().size(), 2);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->qualifiedIdentifier().toString(), QString("foo::operator-"));

    // unary operator -
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().size(), 1);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().begin()->size(), 1);
  }
}

void TestDUChain::testADLNameAlias()
{
  {
    QByteArray adlCall("namespace foo { struct A { }; void bar(A &) {} }"
                       "typedef foo::A B;"
                       "int test() { B a; bar(a); }"); // calls foo::bar
    
    LockedTopDUContext top( parse(adlCall, DumpNone) );
    
    QCOMPARE(top->childContexts().count(), 3);
    
    // foo::bar has 1 use
    QCOMPARE(top->childContexts()[0]->localDeclarations().size(), 2);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->qualifiedIdentifier().toString(), QString("foo::bar"));
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().size(), 1);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().begin()->size(), 1);
  }
  {
    QByteArray adlCall("namespace foo { struct A { }; typedef A B; void bar(A &) {}  }"
                       "int test() { foo::B a; bar(a); }"); // calls foo::bar
    
    LockedTopDUContext top( parse(adlCall, DumpNone) );
    
    QCOMPARE(top->childContexts().count(), 3);
    
    // foo::bar has 1 use
    QCOMPARE(top->childContexts()[0]->localDeclarations().size(), 3);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[2]->qualifiedIdentifier().toString(), QString("foo::bar"));
    QCOMPARE(top->childContexts()[0]->localDeclarations()[2]->uses().size(), 1);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[2]->uses().begin()->size(), 1);
  }
  {
    // typedef names do not participate with their namespace
    QByteArray nonAdlCall("struct A { }; namespace foo { typedef A B; void bar(A &) {}  }"
                          "int test() { foo::B a; bar(a); }"); // fails
    
    LockedTopDUContext top( parse(nonAdlCall, DumpNone) );
    
    QCOMPARE(top->childContexts().count(), 4);
    
    // foo::bar has 1 use
    QCOMPARE(top->childContexts()[1]->localDeclarations().size(), 2);
    QCOMPARE(top->childContexts()[1]->localDeclarations()[1]->qualifiedIdentifier().toString(), QString("foo::bar"));
    QCOMPARE(top->childContexts()[1]->localDeclarations()[1]->uses().size(), 0);
  }
}

void TestDUChain::testADLTemplateArguments()
{
  {
    QByteArray adlCall("struct A { };"
                       "namespace foo { template<class T> struct B { }; template<class T> void bar(T &) {} }"
                       "int test() { foo::B<A> a; bar(a); }"); // calls foo::bar
    
    LockedTopDUContext top( parse(adlCall, DumpNone) );
    
    Declaration* d = findDeclaration(top, QualifiedIdentifier("foo::bar<foo::B<A> >"));
    QVERIFY(d);    
    QCOMPARE(d->uses().size(), 1);
    QCOMPARE(d->uses().begin()->size(), 1);
  }
  {
    QByteArray adlCall("template<class T> struct B { };"
                       "namespace foo { struct A { }; template<class T> void bar(T &) {} }"
                       "int test() { B<foo::A> a; bar(a); }"); // calls foo::bar

    LockedTopDUContext top( parse(adlCall, DumpNone) );

    Declaration* d = findDeclaration(top, QualifiedIdentifier("foo::bar<B<foo::A> >"));
    QVERIFY(d);
    QCOMPARE(d->uses().size(), 1);
    QCOMPARE(d->uses().begin()->size(), 1);
  }
  {
    QByteArray adlCall("template<class T> struct B { };"
                       "namespace foo { struct A { }; template<class T> void bar(T &) {} }"
                       "int test() { B<B<foo::A> > a; bar(a); }"); // calls foo::bar
    
    LockedTopDUContext top( parse(adlCall, DumpNone) );
    
    Declaration* d = findDeclaration(top, QualifiedIdentifier("foo::bar<B<B<foo::A> > >"));
    QVERIFY(d);
    QCOMPARE(d->uses().size(), 1);
    QCOMPARE(d->uses().begin()->size(), 1);
  }
  {
    QByteArray adlCall("template<class T> struct B { };"
                       "namespace foo { struct A { }; template<class T> void bar(T &) {} }"
                       "int test() { B<foo::A> a; bar(a); }"); // calls foo::bar
    
    LockedTopDUContext top( parse(adlCall, DumpNone) );
    
    Declaration* d = findDeclaration(top, QualifiedIdentifier("foo::bar<B<foo::A> >"));
    QVERIFY(d);
    QCOMPARE(d->uses().size(), 1);
    QCOMPARE(d->uses().begin()->size(), 1);
  }
  {
    QByteArray adlCall("namespace foo { enum E { value }; template<class T> void bar(T &) {} }"
                       "template<class T> struct B { };"
                       "int test() { B<foo::E> a; bar(a); }");
    
    LockedTopDUContext top( parse(adlCall, DumpNone) );
    
    Declaration* d = findDeclaration(top, QualifiedIdentifier("foo::bar<B<foo::E> >"));
    QVERIFY(d);
    QCOMPARE(d->uses().size(), 1);
    QCOMPARE(d->uses().begin()->size(), 1);
  }
  {
    QByteArray nonAdlCall("namespace foo { enum E { value }; template<class T> void bar(T &) {} }"
                          "template<int I> struct B { };"
                          "int test() { B<foo::value> a; bar(a); }");

    LockedTopDUContext top( parse(nonAdlCall, DumpNone) );

    Declaration* d = findDeclaration(top, QualifiedIdentifier("foo::bar<B<foo::value> >"));
    QVERIFY(d);
    QCOMPARE(d->uses().size(), 0);
  }
  {
    QByteArray nonAdlCall("namespace foo { struct A {}; void f(A& a) {} template<class T> void bar(T &) {} }"
                          "template<void (*I)(foo::A&)> struct B { };"
                          "int test() { B<&foo::f> a; bar(a); }");

    LockedTopDUContext top( parse(nonAdlCall, DumpNone) );

    Declaration* d = findDeclaration(top, QualifiedIdentifier("foo::bar<B<&foo::f> >"));
    QVERIFY(d);
    QCOMPARE(d->uses().size(), 0);
  }
}

void TestDUChain::testADLTemplateTemplateArguments() {
  {
    QByteArray adlCall("namespace foo { struct A {}; template<class T> void bar(T &) {} }"
                       "struct C : public foo::A { };" // foo::A is associated class of C
                       "template<class T> struct B { };"
                       "int test() { B<C> a; bar(a); }"); // in this ADL call
                        
    LockedTopDUContext top( parse(adlCall, DumpNone) );
    
    Declaration* d = findDeclaration(top, QualifiedIdentifier("foo::bar<B<C> >"));
    QVERIFY(d);
    QCOMPARE(d->uses().size(), 1);
    QCOMPARE(d->uses().begin()->size(), 1);
  }
  {
    QByteArray nonAdlCall("namespace foo { struct A {}; template<class T> void bar(T &) {} }"
                          "template<class T> struct C : public foo::A { };" // foo::A is not an associated class of C ...
                          "template<template<class U> class T> struct B { };"
                          "int test() { B<C> a; bar(a); }"); // in this ADL call
    
    LockedTopDUContext top( parse(nonAdlCall, DumpNone) );
    
    Declaration* d = findDeclaration(top, QualifiedIdentifier("foo::bar<B<C> >"));
    QVERIFY(d);
    QCOMPARE(d->uses().size(), 0);
  }
}

void TestDUChain::testADLEllipsis()
{
  LockedTopDUContext top( parse(readCodeFile("testADLEllipsis.cpp"), DumpNone) );
  QCOMPARE(top->localDeclarations().size(), 5);
  QCOMPARE(top->localDeclarations().at(0)->uses().size(), 1);
  QCOMPARE(top->localDeclarations().at(0)->uses().begin().value().size(), 1);
  QCOMPARE(top->localDeclarations().at(1)->uses().size(), 1);
  QCOMPARE(top->localDeclarations().at(1)->uses().begin().value().size(), 3);
  QCOMPARE(top->localDeclarations().at(2)->uses().size(), 1);
  QCOMPARE(top->localDeclarations().at(2)->uses().begin().value().size(), 1);
}


void TestDUChain::testAssignmentOperators()
{
  QString operators("class foo {\n");
  QStringList operatorPrefixes;
  operatorPrefixes << "*" << "+" << "-" << "/" << "&" << "|" << "<<" << ">>" << "^" << "";
  foreach ( const QString& op, operatorPrefixes ) {
    operators.append("  foo& operator" + op + "=(const foo& other) { };\n");
  }

  operators.append("};\n\nvoid main(int, char**) {\n");
  operators.append("  foo a, b;");
  foreach ( const QString& op, operatorPrefixes ) {
    operators.append("  a " + op + "= b;\n");
  }
  operators.append("}\n");

  LockedTopDUContext top( parse(operators.toAscii(), DumpAll) );

  QCOMPARE(top->childContexts().count(), 3);

  QCOMPARE(top->childContexts()[0]->localDeclarations().size(), operatorPrefixes.size());
  for ( int i = 0; i < operatorPrefixes.size(); i ++ ) {
    QCOMPARE(top->childContexts()[0]->localDeclarations()[i]->uses().size(), 1);
  }
}

#define V_CHILD_COUNT(context, cnt) QCOMPARE(context->childContexts().count(), cnt)
#define V_DECLARATION_COUNT(context, cnt) QCOMPARE(context->localDeclarations().count(), cnt)

#define V_USE_COUNT(declaration, cnt) QCOMPARE(declaration->uses().count(), 1); QCOMPARE(declaration->uses().begin()->count(), cnt);


DUContext* childContext(DUContext* ctx, const QString& name) {
  foreach(DUContext* child, ctx->childContexts())
    if(child->localScopeIdentifier().toString() == name)
      return child;
  return 0;
}

Declaration* localDeclaration(DUContext* ctx, const QString& name) {
  foreach(Declaration* decl, ctx->localDeclarations())
    if(decl->identifier().toString() == name)
      return decl;
  return 0;
}

void TestDUChain::testUsingDeclaration()
{
  TEST_FILE_PARSE_ONLY

  //                 0         1         2         3         4         5         6         7
  //                 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012
  QByteArray method("namespace n1 { class C{}; C c2; } namespace n2{ using n1::C; using n1::c2; C c = c2; }");

  LockedTopDUContext top = parse(method, DumpNone);

  V_CHILD_COUNT(top, 2);
  QVERIFY(childContext(top, "n1"));
  V_DECLARATION_COUNT(childContext(top, "n1"), 2);
  QVERIFY(childContext(top, "n2"));
  V_DECLARATION_COUNT(childContext(top, "n2"), 3);
  V_USE_COUNT(top->childContexts()[0]->localDeclarations()[0], 3);
  V_USE_COUNT(top->childContexts()[0]->localDeclarations()[1], 2);
  QCOMPARE(localDeclaration(childContext(top, "n2"), "c")->abstractType()->indexed(), top->childContexts()[0]->localDeclarations()[0]->abstractType()->indexed());

}

void TestDUChain::testUsingDeclarationInTemplate()
{
  TEST_FILE_PARSE_ONLY

  //                 0         1         2         3         4         5         6         7
  //                 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012
  QByteArray method("template<class T> class A { T i; }; template<class Q> struct B: private A<Q> { using A<Q>::i; };");

  LockedTopDUContext top = parse(method, DumpNone);

  Declaration* basicDecl = findDeclaration(top, QualifiedIdentifier("B::i"));
  QVERIFY(basicDecl);
  kDebug() << typeid(*basicDecl).name() << basicDecl->toString();
  QVERIFY(basicDecl->abstractType());
  kDebug() << basicDecl->abstractType()->toString();

  Declaration* decl = findDeclaration(top, QualifiedIdentifier("B<int>::i"));
  QVERIFY(decl);
  QVERIFY(decl->abstractType());
  QVERIFY(decl->type<IntegralType>());

}

void TestDUChain::testDeclareUsingNamespace2()
{
  TEST_FILE_PARSE_ONLY

  //                 0         1         2         3         4         5         6         7
  //                 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012
  QByteArray method("namespace foo2 {int bar2; namespace SubFoo { int subBar2; } }; namespace foo { int bar; using namespace foo2; } namespace GFoo{ namespace renamedFoo2 = foo2; using namespace renamedFoo2; using namespace SubFoo; int gf; } using namespace GFoo; int test() { return bar; }");

  LockedTopDUContext top = parse(method, DumpNone);

  return;

  QVERIFY(!top->parentContext());
  QCOMPARE(top->childContexts().count(), 5);
  QCOMPARE(top->localDeclarations().count(), 5);
  QVERIFY(top->localScopeIdentifier().isEmpty());
  QVERIFY(findDeclaration(top, Identifier("foo")));

  QCOMPARE( top->childContexts().first()->localDeclarations().count(), 2);
  Declaration* bar2 = top->childContexts().first()->localDeclarations()[0];
  QVERIFY(bar2);
  QCOMPARE(bar2->identifier(), Identifier("bar2"));

  QCOMPARE( top->childContexts()[1]->localDeclarations().count(), 2);
  Declaration* bar = top->childContexts()[1]->localDeclarations()[0];
  QVERIFY(bar);
  QCOMPARE(bar->identifier(), Identifier("bar"));

  QCOMPARE( top->childContexts()[2]->localDeclarations().count(), 4);
  Declaration* gf = top->childContexts()[2]->localDeclarations()[3];
  QVERIFY(gf);
  QCOMPARE(gf->identifier(), Identifier("gf"));

  //QCOMPARE(top->namespaceAliases().count(), 1);
  //QCOMPARE(top->namespaceAliases().first()->nsIdentifier, QualifiedIdentifier("foo"));

  //QCOMPARE(findDeclaration(top, QualifiedIdentifier("bar2")), bar2);
  QCOMPARE(findDeclaration(top, QualifiedIdentifier("::bar")), noDef);
  QCOMPARE(findDeclaration(top, QualifiedIdentifier("foo::bar2")), bar2);
  QCOMPARE(findDeclaration(top, QualifiedIdentifier("foo2::bar2")), bar2);
  QCOMPARE(findDeclaration(top, QualifiedIdentifier("::foo::bar2")), bar2);

  //bar2 can be found from GFoo
  QCOMPARE(findDeclaration(top->childContexts()[2], QualifiedIdentifier("bar2")), bar2);
  QCOMPARE(findDeclaration(top->childContexts()[2], QualifiedIdentifier("bar")), noDef);
  QCOMPARE(findDeclaration(top, QualifiedIdentifier("gf")), gf);
  QCOMPARE(findDeclaration(top, QualifiedIdentifier("bar2")), bar2);
  QCOMPARE(findDeclaration(top, QualifiedIdentifier("::GFoo::renamedFoo2::bar2")), bar2);
  QVERIFY(findDeclaration(top, QualifiedIdentifier("subBar2")) != noDef);

}

void TestDUChain::testGlobalNamespaceAlias()
{
  QByteArray method("namespace foo { int bar(); } namespace afoo = foo; int test() { afoo::bar(); }");

  LockedTopDUContext top( parse(method, DumpNone) );

  QCOMPARE(top->childContexts().count(), 3);

  QCOMPARE(top->localDeclarations().size(), 3);
  NamespaceAliasDeclaration* aliasDecl = dynamic_cast<NamespaceAliasDeclaration*>(top->localDeclarations()[1]);
  QVERIFY(aliasDecl);
  QCOMPARE(aliasDecl->importIdentifier(), QualifiedIdentifier("foo"));
  QCOMPARE(aliasDecl->identifier(), Identifier("afoo"));

  QCOMPARE(top->childContexts()[0]->localDeclarations().size(), 1);
  QCOMPARE(top->childContexts()[0]->localDeclarations()[0]->uses().size(), 1);
  QCOMPARE(top->childContexts()[0]->localDeclarations()[0]->uses().begin()->size(), 1);
}

void TestDUChain::testExternalMemberDeclaration()
{
  QByteArray method("struct A { static int m; };\n"
                                      "int A::m = 3;");
  
  LockedTopDUContext top( parse(method, DumpAll) );
  
  // The declaration of "A::m" is put into a helper-context that makes the scope "A::"
  QCOMPARE(top->localDeclarations().size(), 1);
  QCOMPARE(top->childContexts().size(), 2);
  QCOMPARE(top->localDeclarations()[0]->uses().size(), 1);
  // There must be a use for the "A" in "int A::m"
  QCOMPARE(top->localDeclarations()[0]->uses().begin()->size(), 1);
  QCOMPARE(top->localDeclarations()[0]->uses().begin()->at(0).castToSimpleRange().textRange(), KTextEditor::Range(1, 4, 1, 5));
}

void TestDUChain::testUsingGlobalNamespaceAlias()
{
  QByteArray method("namespace foo { int bar(); } namespace afoo = foo; int test() { using namespace afoo; bar(); }");

  LockedTopDUContext top( parse(method, DumpNone) );

  QCOMPARE(top->childContexts().count(), 3);

  QCOMPARE(top->localDeclarations().size(), 3);
  NamespaceAliasDeclaration* aliasDecl = dynamic_cast<NamespaceAliasDeclaration*>(top->localDeclarations()[1]);
  QVERIFY(aliasDecl);
  QCOMPARE(aliasDecl->importIdentifier(), QualifiedIdentifier("foo"));
  QCOMPARE(aliasDecl->identifier(), Identifier("afoo"));

  QCOMPARE(top->childContexts()[0]->localDeclarations().size(), 1);
  QCOMPARE(top->childContexts()[0]->localDeclarations()[0]->uses().size(), 1);
  QCOMPARE(top->childContexts()[0]->localDeclarations()[0]->uses().begin()->size(), 1);
}

void TestDUChain::testGlobalNamespaceAliasCycle()
{
  QByteArray method("namespace foo { int bar(); } namespace A = foo; namespace B = A; namespace A = B; "
                    "int test() { foo::bar(); A::bar(); B::bar(); } ");

  LockedTopDUContext top( parse(method, DumpNone) );

  QCOMPARE(top->childContexts().count(), 3);

  QCOMPARE(top->localDeclarations().size(), 5); // foo, A, B, A, test
  NamespaceAliasDeclaration* aliasDecl1 = dynamic_cast<NamespaceAliasDeclaration*>(top->localDeclarations()[1]);
  QVERIFY(aliasDecl1);
  QCOMPARE(aliasDecl1->importIdentifier(), QualifiedIdentifier("foo"));
  QCOMPARE(aliasDecl1->identifier(), Identifier("A"));
  NamespaceAliasDeclaration* aliasDecl2 = dynamic_cast<NamespaceAliasDeclaration*>(top->localDeclarations()[2]);
  QVERIFY(aliasDecl2);
  QCOMPARE(aliasDecl2->importIdentifier(), QualifiedIdentifier("foo")); // already resolved
  QCOMPARE(aliasDecl2->identifier(), Identifier("B"));
  NamespaceAliasDeclaration* aliasDecl3 = dynamic_cast<NamespaceAliasDeclaration*>(top->localDeclarations()[3]);
  QVERIFY(aliasDecl3);
  QCOMPARE(aliasDecl3->importIdentifier(), QualifiedIdentifier("foo")); // already resolved
  QCOMPARE(aliasDecl3->identifier(), Identifier("A"));

  QCOMPARE(top->childContexts()[0]->localDeclarations().size(), 1);
  QCOMPARE(top->childContexts()[0]->localDeclarations()[0]->uses().size(), 1);
  QCOMPARE(top->childContexts()[0]->localDeclarations()[0]->uses().begin()->size(), 3);
}

void TestDUChain::testUsingGlobalNamespaceAliasCycle()
{
  QByteArray method("namespace foo { int bar(); } namespace A = foo; namespace B = A; namespace A = B; "
                    "int testA() { using namespace A; bar(); } "
                    "int testB() { using namespace B; bar(); }");

  LockedTopDUContext top( parse(method, DumpNone) );

  QCOMPARE(top->childContexts().count(), 5);

  QCOMPARE(top->localDeclarations().size(), 6); // foo, A, B, A, testA, testB
  NamespaceAliasDeclaration* aliasDecl1 = dynamic_cast<NamespaceAliasDeclaration*>(top->localDeclarations()[1]);
  QVERIFY(aliasDecl1);
  QCOMPARE(aliasDecl1->importIdentifier(), QualifiedIdentifier("foo"));
  QCOMPARE(aliasDecl1->identifier(), Identifier("A"));
  NamespaceAliasDeclaration* aliasDecl2 = dynamic_cast<NamespaceAliasDeclaration*>(top->localDeclarations()[2]);
  QVERIFY(aliasDecl2);
  QCOMPARE(aliasDecl2->importIdentifier(), QualifiedIdentifier("foo")); // already resolved
  QCOMPARE(aliasDecl2->identifier(), Identifier("B"));
  NamespaceAliasDeclaration* aliasDecl3 = dynamic_cast<NamespaceAliasDeclaration*>(top->localDeclarations()[3]);
  QVERIFY(aliasDecl3);
  QCOMPARE(aliasDecl3->importIdentifier(), QualifiedIdentifier("foo")); // already resolved
  QCOMPARE(aliasDecl3->identifier(), Identifier("A"));

  QCOMPARE(top->childContexts()[0]->localDeclarations().size(), 1);
  QCOMPARE(top->childContexts()[0]->localDeclarations()[0]->uses().size(), 1);
  QCOMPARE(top->childContexts()[0]->localDeclarations()[0]->uses().begin()->size(), 2);
}

void TestDUChain::testLocalNamespaceAlias()
{
  QByteArray method("namespace foo { int bar(); } int test() { namespace afoo = foo; afoo::bar(); }");

  LockedTopDUContext top( parse(method, DumpAll) );

  QCOMPARE(top->childContexts().count(), 3);
  QCOMPARE(top->childContexts()[2]->localDeclarations().size(), 1);
  NamespaceAliasDeclaration* aliasDecl = dynamic_cast<NamespaceAliasDeclaration*>(top->childContexts()[2]->localDeclarations()[0]);
  QVERIFY(aliasDecl);
  QCOMPARE(aliasDecl->importIdentifier(), QualifiedIdentifier("foo"));
  QCOMPARE(aliasDecl->identifier(), Identifier("afoo"));
  QCOMPARE(top->childContexts()[0]->localDeclarations().size(), 1);
  
  QEXPECT_FAIL("", "Local namespace aliases currently don't work, bug 207548", Abort);
  QCOMPARE(top->childContexts()[0]->localDeclarations()[0]->uses().size(), 1);
  QCOMPARE(top->childContexts()[0]->localDeclarations()[0]->uses().begin()->size(), 1);
}

void TestDUChain::testDeclareUsingNamespace()
{
  TEST_FILE_PARSE_ONLY

  //                 0         1         2         3         4         5         6         7
  //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
  QByteArray method("namespace foo { int bar; } using namespace foo; namespace alternativeFoo = foo; int test() { return bar; }");

  LockedTopDUContext top = parse(method, DumpNone);

  QVERIFY(!top->parentContext());
  QCOMPARE(top->childContexts().count(), 3);
  QCOMPARE(top->localDeclarations().count(), 4);
  QVERIFY(top->localScopeIdentifier().isEmpty());
  QVERIFY(findDeclaration(top, Identifier("foo")));
  QCOMPARE(top->localDeclarations()[0]->uses().size(), 1);
  QCOMPARE(top->localDeclarations()[0]->uses().begin()->size(), 2);
  QCOMPARE(top->localDeclarations()[0]->uses().begin()->at(0), RangeInRevision(0, 65-22, 0, 68-22));
  QCOMPARE(top->localDeclarations()[0]->uses().begin()->at(1), RangeInRevision(0, 97-22, 0, 100-22));


//   QCOMPARE(top->localDeclarations()[0]->range()
  QCOMPARE(top->localDeclarations()[1]->range(), RangeInRevision(0, 33, 0, 42));
  kDebug() << top->localDeclarations()[2]->range().castToSimpleRange().textRange();
  QCOMPARE(top->localDeclarations()[2]->range(), RangeInRevision(0, 58, 0, 72));
  
  
  DUContext* fooCtx = top->childContexts().first();
  QCOMPARE(fooCtx->childContexts().count(), 0);
  QCOMPARE(fooCtx->localDeclarations().count(), 1);
  QCOMPARE(fooCtx->localScopeIdentifier(), QualifiedIdentifier("foo"));
  QCOMPARE(fooCtx->scopeIdentifier(), QualifiedIdentifier("foo"));

  Declaration* bar = fooCtx->localDeclarations().first();
  QVERIFY(!bar->isFunctionDeclaration());
  QCOMPARE(bar->identifier(), Identifier("bar"));
  QCOMPARE(bar->qualifiedIdentifier(), QualifiedIdentifier("foo::bar"));
  QCOMPARE(bar->uses().count(), 1);
  QCOMPARE(bar->uses().begin()->count(), 1);
  //kDebug() << findDeclaration(top, bar->identifier(), top->range().start)->qualifiedIdentifier().toString();
  QCOMPARE(findDeclaration(top, bar->identifier(), top->range().start), noDef);
  QCOMPARE(findDeclaration(top, bar->identifier()), bar);
  QCOMPARE(findDeclaration(top, bar->qualifiedIdentifier()), bar);

  QCOMPARE(findDeclaration(top, QualifiedIdentifier("bar")), bar);
  QCOMPARE(findDeclaration(top, QualifiedIdentifier("::bar")), bar); //iso c++ 3.4.3 says this must work
  QCOMPARE(findDeclaration(top, QualifiedIdentifier("foo::bar")), bar);
  QCOMPARE(findDeclaration(top, QualifiedIdentifier("::foo::bar")), bar);
  QCOMPARE(findDeclaration(top, QualifiedIdentifier("alternativeFoo::bar")), bar);
  QCOMPARE(findDeclaration(top, QualifiedIdentifier("::alternativeFoo::bar")), bar);

  DUContext* testCtx = top->childContexts()[2];
  QVERIFY(testCtx->parentContext());
  QCOMPARE(testCtx->importedParentContexts().count(), 1);
  QCOMPARE(testCtx->childContexts().count(), 0);
  QCOMPARE(testCtx->localDeclarations().count(), 0);
  QCOMPARE(testCtx->localScopeIdentifier(), QualifiedIdentifier("test"));
  QCOMPARE(testCtx->scopeIdentifier(), QualifiedIdentifier("test"));

}

void TestDUChain::testSignalSlotDeclaration() {
  {
    QByteArray text("class C {__qt_signals__: void signal2(); public __qt_slots__: void slot2();}; ");

    LockedTopDUContext top = parse(text, DumpNone);

    QCOMPARE(top->localDeclarations().count(), 1);
    QCOMPARE(top->childContexts().count(), 1);
    QCOMPARE(top->childContexts()[0]->localDeclarations().count(), 2);

    ClassFunctionDeclaration* classFun = dynamic_cast<ClassFunctionDeclaration*>(top->childContexts()[0]->localDeclarations()[0]);
    QVERIFY(classFun);
    QVERIFY(classFun->accessPolicy() == ClassMemberDeclaration::Protected);
    QVERIFY(classFun->isSignal());

    classFun = dynamic_cast<ClassFunctionDeclaration*>(top->childContexts()[0]->localDeclarations()[1]);
    QVERIFY(classFun);
    QVERIFY(classFun->accessPolicy() == ClassMemberDeclaration::Public);
    QVERIFY(classFun->isSlot());

  }
  {
    QByteArray text("namespace A { class B;} class Q { public __qt_slots__: void slot(A::B b, const Q* q); }; ");

    LockedTopDUContext top = parse(text, DumpNone);

    QCOMPARE(top->localDeclarations().count(), 2);
    QCOMPARE(top->childContexts().count(), 2);
    QCOMPARE(top->childContexts()[1]->localDeclarations().count(), 1);
    QtFunctionDeclaration* qtDecl = dynamic_cast<QtFunctionDeclaration*>(top->childContexts()[1]->localDeclarations()[0]);
    QVERIFY(qtDecl);
    QCOMPARE(qtDecl->normalizedSignature().str(), QString("A::B,const Q*"));

  }
  {
    // should be valid signals even if the arguments are not found
    QByteArray text("class C {__qt_signals__: void signal2(NotFound); public __qt_slots__: void slot2(NotFound);}; ");

    LockedTopDUContext top = parse(text, DumpNone);

    QCOMPARE(top->localDeclarations().count(), 1);
    QCOMPARE(top->childContexts().count(), 1);
    QCOMPARE(top->childContexts()[0]->localDeclarations().count(), 2);

    ClassFunctionDeclaration* classFun = dynamic_cast<ClassFunctionDeclaration*>(top->childContexts()[0]->localDeclarations()[0]);
    QVERIFY(classFun);
    QVERIFY(classFun->accessPolicy() == ClassMemberDeclaration::Protected);
    QVERIFY(classFun->isSignal());

    classFun = dynamic_cast<ClassFunctionDeclaration*>(top->childContexts()[0]->localDeclarations()[1]);
    QVERIFY(classFun);
    QVERIFY(classFun->accessPolicy() == ClassMemberDeclaration::Public);
    QVERIFY(classFun->isSlot());

  }
}

void TestDUChain::testSignalSlotUse() {
  {
    QByteArray text("class TE; class QObject { void connect(QObject* from, const char* signal, QObject* to, const char* slot); void connect(QObject* from, const char* signal, const char* slot); }; class A : public QObject { public __qt_slots__: void slot1(); void slot2(TE*); __qt_signals__: void signal1(TE*, char);void signal2(); public: void test() { \nconnect(this, __qt_signal__( signal1(TE*, const char&)), this, __qt_slot__(slot2(TE*))); \nconnect(this, __qt_signal__(signal2()), \n__qt_slot__(slot1())); } };");

    LockedTopDUContext top = parse(text, DumpNone);

    QCOMPARE(top->localDeclarations().count(), 3);
    QCOMPARE(top->childContexts().count(), 2);
    QCOMPARE(top->childContexts()[1]->localDeclarations().count(), 5);
    QtFunctionDeclaration* qtDecl = dynamic_cast<QtFunctionDeclaration*>(top->childContexts()[1]->localDeclarations()[0]);
    QVERIFY(qtDecl);
    QVERIFY(top->childContexts()[1]->localDeclarations()[0]->uses().count());
    //kDebug() << top->childContexts()[1]->localDeclarations()[0]->uses().begin()->first().textRange();
    //kDebug() << top->childContexts()[1]->localDeclarations()[1]->uses().begin()->first().textRange();
    //kDebug() << top->childContexts()[1]->localDeclarations()[2]->uses().begin()->first().textRange();
    //kDebug() << top->childContexts()[1]->localDeclarations()[3]->uses().begin()->first().textRange();
    QCOMPARE(top->childContexts()[1]->localDeclarations()[0]->uses().begin()->first().castToSimpleRange().textRange(), RangeInRevision(3, 12, 3, 17).castToSimpleRange().textRange());
    QVERIFY(top->childContexts()[1]->localDeclarations()[1]->uses().count());
    QCOMPARE(top->childContexts()[1]->localDeclarations()[1]->uses().begin()->first().castToSimpleRange().textRange(), RangeInRevision(1, 75, 1, 80).castToSimpleRange().textRange());
    QVERIFY(top->childContexts()[1]->localDeclarations()[2]->uses().count());
    QCOMPARE(top->childContexts()[1]->localDeclarations()[2]->uses().begin()->first(), RangeInRevision(1, 29, 1, 36));
    QVERIFY(top->childContexts()[1]->localDeclarations()[3]->uses().count());
    QCOMPARE(top->childContexts()[1]->localDeclarations()[3]->uses().begin()->first(), RangeInRevision(2, 28, 2, 35));

    QCOMPARE(top->localDeclarations()[0]->uses().count(), 1);
    QCOMPARE(top->localDeclarations()[0]->uses().begin()->count(), 4);

  }
  {
    QByteArray text("class QObject { void connect(QObject* from, const char* signal, QObject* to, const char* slot); void connect(QObject* from, const char* signal, const char* slot); }; struct AA : QObject { __qt_signals__: void signal1(int); void signal2(); };struct T{operator AA*() const; };class A : AA { public __qt_slots__: void slot1(); void slot2(); __qt_signals__: void signal1();public: void test() {T t;connect(t, __qt_signal__(signal2()), this, __qt_slot__(slot2()));connect(this, __qt_signal__(signal1(int)), __qt_slot__(slot1())); } }; ");

    LockedTopDUContext top = parse(text, DumpNone);

    QCOMPARE(top->localDeclarations().count(), 4);
    QCOMPARE(top->childContexts().count(), 4);
    QCOMPARE(top->childContexts()[3]->localDeclarations().count(), 4);
    QVERIFY(top->childContexts()[3]->localDeclarations()[0]->uses().count());
    QVERIFY(top->childContexts()[3]->localDeclarations()[1]->uses().count());
    QVERIFY(!top->childContexts()[3]->localDeclarations()[2]->uses().count()); //signal1() is not used

    QCOMPARE(top->childContexts()[1]->localDeclarations().count(), 2);
    ClassDeclaration* classAA = dynamic_cast<ClassDeclaration*>(top->localDeclarations()[1]);
    QVERIFY(classAA);
    QCOMPARE(classAA->baseClassesSize(), 1u);
    QCOMPARE(classAA->baseClasses()[0].access, Declaration::Public);
    ClassDeclaration* classA = dynamic_cast<ClassDeclaration*>(top->localDeclarations()[3]);
    QVERIFY(classA);
    QCOMPARE(classA->baseClassesSize(), 1u);
    QCOMPARE(classA->baseClasses()[0].access, Declaration::Private);

    QVERIFY(top->childContexts()[1]->localDeclarations()[0]->uses().count());
    QVERIFY(top->childContexts()[1]->localDeclarations()[1]->uses().count());

  }
  {
    // test signals without full signal signature specification
    QByteArray text("class QObject { void connect(QObject* from, const char* signal, QObject* to, const char* slot);\n"
                    "  void connect(QObject* from, const char* signal, const char* slot); };\n"
                    "struct AA : QObject { __qt_signals__: void signal1(bool arg1 = false); };\n"
                    "class A : AA { public __qt_slots__: void slot1();\n"
                    "  public: void test() { connect(this, __qt_signal__(signal1()), this, __qt_slot__(slot1()));} };");
    LockedTopDUContext top = parse(text, DumpNone);

    QVERIFY(top->problems().isEmpty());

    QCOMPARE(top->localDeclarations().count(), 3);
    QCOMPARE(top->childContexts().count(), 3);
    QCOMPARE(top->childContexts().at(1)->localDeclarations().size(), 1);
    ClassFunctionDeclaration* sig = dynamic_cast<ClassFunctionDeclaration*>(top->childContexts().at(1)->localDeclarations().first());
    QVERIFY(sig);
    QVERIFY(sig->identifier() == Identifier("signal1"));
    QVERIFY(sig->isSignal());
    QCOMPARE(sig->uses().size(), 1);
    QCOMPARE(sig->uses().begin()->count(), 1);
    QCOMPARE(sig->uses().begin()->first(), RangeInRevision(4, 52, 4, 59));
  }
  {
    QByteArray text("int main() { const char* signal; signal = __qt_signal__(someSignal()); }");

    LockedTopDUContext top = parse(text, DumpNone);

    QCOMPARE(top->localDeclarations().count(), 1);
    QCOMPARE(top->childContexts().count(), 2);
    QCOMPARE(top->childContexts().at(1)->localDeclarations().size(), 1);
    Declaration* sig =
        dynamic_cast<Declaration*>(top->childContexts().at(1)->localDeclarations().first());
    QVERIFY(sig);
    QVERIFY(sig->identifier() == Identifier("signal"));
    QCOMPARE(sig->uses().size(), 1);
    QCOMPARE(sig->uses().begin()->count(), 1);
    QCOMPARE(sig->uses().begin()->first(), RangeInRevision(0, 33, 0, 39));
  }
}

void TestDUChain::testFunctionDefinition() {
  //               0         1         2         3         4         5         6         7
  //               01234567890123456789012345678901234567890123456789012345678901234567890123456789
  QByteArray text("class B{}; class A { char at(B* b); A(); ~A(); }; \n char A::at(B* b) {B* b; at(b); }; A::A() : i(3) {}; A::~A() {}; ");

  LockedTopDUContext top = parse(text, DumpNone);

  QCOMPARE(top->childContexts().count(), 8);
  QCOMPARE(top->childContexts()[1]->childContexts().count(), 3);
  QCOMPARE(top->childContexts()[1]->localDeclarations().count(), 3);

  QCOMPARE(top->childContexts()[4]->type(), DUContext::Function);

  Declaration* atInA = top->childContexts()[1]->localDeclarations()[0];

  QVERIFY(dynamic_cast<AbstractFunctionDeclaration*>(atInA));

  QVERIFY(atInA->internalContext());
  QCOMPARE(atInA->internalContext()->range(), RangeInRevision(0, 29, 0, 33));
  
  QCOMPARE(top->localDeclarations().count(), 5);

  QVERIFY(top->localDeclarations()[1]->logicalInternalContext(top));
  QVERIFY(!top->childContexts()[1]->localDeclarations()[0]->isDefinition());
  QVERIFY(!top->childContexts()[1]->localDeclarations()[1]->isDefinition());
  QVERIFY(!top->childContexts()[1]->localDeclarations()[2]->isDefinition());

  QVERIFY(top->localDeclarations()[2]->isDefinition());
  QVERIFY(top->localDeclarations()[3]->isDefinition());
  QVERIFY(top->localDeclarations()[4]->isDefinition());

  QVERIFY(top->localDeclarations()[2]->internalContext());
  QVERIFY(top->localDeclarations()[3]->internalContext());
  QVERIFY(top->localDeclarations()[4]->internalContext());

  QCOMPARE(top->localDeclarations()[2]->internalContext()->owner(), top->localDeclarations()[2]);
  QCOMPARE(top->localDeclarations()[3]->internalContext()->owner(), top->localDeclarations()[3]);
  QCOMPARE(top->localDeclarations()[4]->internalContext()->owner(), top->localDeclarations()[4]);

  QVERIFY(top->localDeclarations()[1]->logicalInternalContext(top));

  QVERIFY(dynamic_cast<FunctionDefinition*>(top->localDeclarations()[2]));
  QCOMPARE(static_cast<FunctionDefinition*>(top->localDeclarations()[2])->declaration(), top->childContexts()[1]->localDeclarations()[0]);
  QCOMPARE(top->localDeclarations()[2], FunctionDefinition::definition(top->childContexts()[1]->localDeclarations()[0]));

  QVERIFY(dynamic_cast<FunctionDefinition*>(top->localDeclarations()[3]));
  QCOMPARE(static_cast<FunctionDefinition*>(top->localDeclarations()[3])->declaration(), top->childContexts()[1]->localDeclarations()[1]);
  QCOMPARE(top->localDeclarations()[3], FunctionDefinition::definition(top->childContexts()[1]->localDeclarations()[1]));
  QCOMPARE(top->childContexts()[5]->owner(), top->localDeclarations()[3]);
  QVERIFY(!top->childContexts()[5]->localScopeIdentifier().isEmpty());

  QVERIFY(top->localDeclarations()[1]->logicalInternalContext(top));
  QVERIFY(dynamic_cast<FunctionDefinition*>(top->localDeclarations()[4]));
  QCOMPARE(static_cast<FunctionDefinition*>(top->localDeclarations()[4])->declaration(), top->childContexts()[1]->localDeclarations()[2]);
  QCOMPARE(top->localDeclarations()[4], FunctionDefinition::definition(top->childContexts()[1]->localDeclarations()[2]));

  QVERIFY(top->localDeclarations()[1]->logicalInternalContext(top));

  QCOMPARE(top->childContexts()[3]->owner(), top->localDeclarations()[2]);
  QCOMPARE(top->childContexts()[3]->importedParentContexts().count(), 1);
  QCOMPARE(top->childContexts()[3]->importedParentContexts()[0].context(0), top->childContexts()[2]);

  QCOMPARE(top->childContexts()[2]->importedParentContexts().count(), 1);
  QCOMPARE(top->childContexts()[2]->importedParentContexts()[0].context(0), top->childContexts()[1]);


  QCOMPARE(findDeclaration(top, QualifiedIdentifier("at")), noDef);

  QVERIFY(top->localDeclarations()[2]->internalContext());
  QCOMPARE(top->localDeclarations()[2]->internalContext()->localDeclarations().count(), 1);

}

Declaration* declaration(Declaration* decl, TopDUContext* top = 0) {
  FunctionDefinition* def = dynamic_cast<FunctionDefinition*>(decl);
  if(!def)
    return 0;
  return def->declaration(top);
}

void TestDUChain::testFunctionDefinition2() {
  {
    QByteArray text("//????\nclass B{B();}; B::B() {} "); //the ???? tests whether the column-numbers are resistant to special characters

    LockedTopDUContext top = parse(text, DumpNone);

    QCOMPARE(top->childContexts().count(), 3);

    QCOMPARE(top->childContexts()[0]->localDeclarations().count(), 1);
    QCOMPARE(top->localDeclarations().count(), 2);

    QCOMPARE(top->childContexts()[0]->localDeclarations()[0], declaration(top->localDeclarations()[1], top->topContext()));

    QCOMPARE(top->childContexts()[1]->type(), DUContext::Function);
    QCOMPARE(top->childContexts()[1]->range().start.column, 20);
    QCOMPARE(top->childContexts()[1]->range().end.column, 20);
    //Many parts of kdevelop assume that the compound parens are included in the range, so it has to stay like that
    QCOMPARE(top->childContexts()[0]->range(), RangeInRevision(1, 7, 1, 13));
    QCOMPARE(top->childContexts()[2]->range(), RangeInRevision(1, 22, 1, 24));

    QVERIFY(!top->childContexts()[2]->inSymbolTable());

  }
  {
    QByteArray text("void test(int a, int cc);"); //the ???? tests whether the column-numbers are resistant to special characters

    LockedTopDUContext top = parse(text, DumpNone);

    QCOMPARE(top->childContexts().count(), 1);

    QCOMPARE(top->childContexts()[0]->localDeclarations().count(), 2);

    QCOMPARE(top->childContexts()[0]->range().start.column, 10);
    QCOMPARE(top->childContexts()[0]->range().end.column, 23);

    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->range().start.column, 21);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->range().end.column, 23);

    QCOMPARE(top->childContexts()[0]->localDeclarations()[0]->range().start.column, 14);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[0]->range().end.column, 15);

  }

  {
    QByteArray text("void test(int, int&);");

    LockedTopDUContext top = parse(text, DumpNone);

    QCOMPARE(top->childContexts().count(), 1);

    QCOMPARE(top->childContexts()[0]->localDeclarations().count(), 2);

  }

}

void TestDUChain::testFunctionDefinition4() {
  QByteArray text("class B{ B(); }; namespace A{class B{B();};} namespace A{ B::B() {} } ");

  LockedTopDUContext top = parse(text, DumpNone);

  QCOMPARE(top->childContexts().count(), 3);

  QCOMPARE(top->childContexts()[1]->localDeclarations().count(), 1);
  QCOMPARE(top->childContexts()[1]->childContexts().count(), 1);
  QCOMPARE(top->childContexts()[1]->childContexts()[0]->localDeclarations().count(), 1);
  QCOMPARE(top->childContexts()[2]->localDeclarations().count(), 1);

  QVERIFY(top->childContexts()[2]->localDeclarations()[0]->isDefinition());
  QCOMPARE(declaration(top->childContexts()[2]->localDeclarations()[0]), top->childContexts()[1]->childContexts()[0]->localDeclarations()[0]);
  //Verify that the function-definition context also imports the correct class context
  QVERIFY(top->childContexts()[2]->localDeclarations()[0]->internalContext()->imports(top->childContexts()[1]->childContexts()[0]));
}

void TestDUChain::testFunctionDefinition3() {
  QByteArray text("class B{template<class T> void test(T t); B(int i); int test(int a); int test(char a); template<class T2, class T3> void test(T2 t, T3 t3); int test(Unknown k); int test(Unknown2 k); }; template<class T> void B::test(T t) {}; B::B(int) {}; int B::test(int a){}; int B::test(char a){}; template<class T2, class T3> void B::test(T2 t, T3 t3) {}; int B::test(Unknown k){}; int B::test( Unknown2 k) {}; ");

  LockedTopDUContext top = parse(text, DumpNone);

  QCOMPARE(top->childContexts().count(), 17);

  QCOMPARE(top->childContexts()[0]->localDeclarations().count(), 7);
  QCOMPARE(top->localDeclarations().count(), 8);

  QVERIFY(top->localDeclarations()[0]->isDefinition()); //Class-declarations are considered definitions too.
  QVERIFY(top->localDeclarations()[1]->isDefinition());
  QVERIFY(top->localDeclarations()[2]->isDefinition());
  QVERIFY(top->localDeclarations()[3]->isDefinition());
  QVERIFY(top->localDeclarations()[4]->isDefinition());
  QVERIFY(top->localDeclarations()[5]->isDefinition());
  QVERIFY(top->localDeclarations()[6]->isDefinition());

  kDebug() << top->childContexts()[0]->localDeclarations()[0]->toString();
  kDebug() << declaration(top->localDeclarations()[1], top->topContext())->toString();

  QCOMPARE(top->childContexts()[0]->localDeclarations()[0], declaration(top->localDeclarations()[1], top->topContext()));
  QCOMPARE(top->childContexts()[0]->localDeclarations()[1], declaration(top->localDeclarations()[2], top->topContext()));
  QCOMPARE(top->childContexts()[0]->localDeclarations()[2], declaration(top->localDeclarations()[3], top->topContext()));
  QCOMPARE(top->childContexts()[0]->localDeclarations()[3], declaration(top->localDeclarations()[4], top->topContext()));
  QCOMPARE(top->childContexts()[0]->localDeclarations()[4], declaration(top->localDeclarations()[5], top->topContext()));
  QCOMPARE(top->childContexts()[0]->localDeclarations()[5], declaration(top->localDeclarations()[6], top->topContext()));

}

void TestDUChain::testFunctionDefinition5() {
  QByteArray text("class Class {typedef Class ClassDef;void test(ClassDef);Class();}; void Class::test(ClassDef i) {Class c;}");
  LockedTopDUContext top = parse(text, DumpNone);

  QCOMPARE(top->childContexts().count(), 3);
  QCOMPARE(top->childContexts()[0]->localDeclarations().count(), 3);
  QCOMPARE(top->childContexts()[0]->localDeclarations()[0]->uses().count(), 1); //Used from 1 file
  QCOMPARE(top->childContexts()[0]->localDeclarations()[0]->uses().begin()->count(), 2); //Used 2 times
  QCOMPARE(top->localDeclarations().count(), 2);
  QCOMPARE(top->childContexts()[2]->localDeclarations().count(), 1);
}

void TestDUChain::testFunctionDefinition6() {
  QByteArray text("class Class {Class(); void test();}; void Class::test(Class c) {int i;}");
  LockedTopDUContext top = parse(text, DumpNone);

  //Here we do an update, since there was a bug where updating caused problems here
  parse(text, DumpNone, top);

  QCOMPARE(top->childContexts().count(), 3);
  QCOMPARE(top->childContexts()[0]->localDeclarations().count(), 2);
  QCOMPARE(top->localDeclarations().count(), 2);
  QCOMPARE(top->childContexts()[1]->localDeclarations().count(), 1);
  QList<Declaration*> decls = top->findDeclarations(QualifiedIdentifier("Class"));
  QCOMPARE(decls.size(), 1);
  kDebug() << "qualified identifier:" << top->childContexts()[0]->localDeclarations()[0]->qualifiedIdentifier();
  QVERIFY(!dynamic_cast<FunctionDefinition*>(top->childContexts()[0]->localDeclarations()[0]));
  QVERIFY(dynamic_cast<ClassFunctionDeclaration*>(top->childContexts()[0]->localDeclarations()[0]));
  QVERIFY(static_cast<ClassFunctionDeclaration*>(top->childContexts()[0]->localDeclarations()[0])->isConstructor());
  kDebug() << top->childContexts()[1]->localDeclarations()[0]->abstractType()->toString();
  QCOMPARE(top->localDeclarations()[1], top->childContexts()[2]->owner());
  QCOMPARE(Cpp::localClassFromCodeContext(top->childContexts()[2]), top->localDeclarations()[0]);
  QCOMPARE(Cpp::localClassFromCodeContext(top->childContexts()[1]), top->localDeclarations()[0]);
  QVERIFY(top->childContexts()[1]->importers().contains(top->childContexts()[2]));
  QCOMPARE(top->childContexts()[1]->localDeclarations()[0]->abstractType()->indexed(), top->localDeclarations()[0]->abstractType()->indexed());
}

void TestDUChain::testFunctionDefinition7() {
  QByteArray text("class ClassA {};\n\
template<typename E_T1>\n\
class ClassE\n\
{\n\
public:\n\
  template<typename E_A_T1>\n\
  void E_FuncA(E_A_T1)\n\
  {\n\
  }\n\
};\n\
template<typename E_T1>\n\
ClassE<E_T1>::~ClassE() = default;\n\
template<>\n\
template<>\n\
void ClassE<ClassA>::E_FuncA<ClassA>(ClassA);\n\
template<>\n\
template<>\n\
void ClassE<ClassA>::E_FuncA<ClassA>(ClassA)\n\
{\n\
}");
  LockedTopDUContext top = parse(text, DumpNone);
  QCOMPARE(top->localDeclarations().count(), 5);
  QVERIFY(dynamic_cast<FunctionDefinition*>(top->localDeclarations()[2]));
  QVERIFY(!dynamic_cast<FunctionDefinition*>(top->localDeclarations()[3]));
  QVERIFY(dynamic_cast<FunctionDefinition*>(top->localDeclarations()[4]));
}

void TestDUChain::testLoopNamespaceImport() {
  QByteArray text("namespace A {int i;} namespace B { using namespace A; } namespace A{ using namespace B; }; using namespace B; void test() { i += 5; }");
  LockedTopDUContext top = parse(text, DumpNone);

  QCOMPARE(top->childContexts().count(), 5);
  QCOMPARE(top->childContexts()[0]->localDeclarations().count(), 1);
  QVERIFY(!top->childContexts()[0]->localDeclarations()[0]->uses().isEmpty());

}

void TestDUChain::testConstructorUses()
{
  {
    QByteArray text = "struct A { A(A); A(A,A); }; void test() { A w; A a(A(w), A(w, w), w); }";
    LockedTopDUContext top = parse(text, DumpAll);
    QCOMPARE(top->childContexts().size(), 3);
    QCOMPARE(top->childContexts()[2]->localDeclarations().size(), 2);
    QCOMPARE(top->childContexts()[2]->localDeclarations()[0]->uses().size(), 1);
    QCOMPARE(top->childContexts()[2]->localDeclarations()[0]->uses().begin()->size(), 4);

    QCOMPARE(top->childContexts()[0]->localDeclarations().size(), 2);

    ///@todo Make these work! (ExpressionVisitor::buildParametersFromDeclaration)
    #if 0
    QCOMPARE(top->childContexts()[0]->localDeclarations()[0].uses().size(), 1);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1].uses().size(), 1);
    #endif

  }
  {
    // NOTE: This test is currently broken because only the 2nd case is reported.
    QByteArray text;
    text += "class Q {\n";
    text += "public:\n";
    text += "  Q(int var) { }\n";
    text += "  Q() { }\n";
    text += "};\n";
    text += "class B : public Q {\n";
    text += "public:\n";
    text += "  B(int var) : Q(var) { }\n";
    text += "  B() : Q() { }\n";
    text += "};\n";
    text += "int main(int argc, char* argv[]) {\n";
    text += "  Q  a1(123);\n";
    text += "  Q  a2 = Q(123);\n";
    text += "  Q *a3 = new Q(123);\n";
    text += "  Q  a4(argc);\n";
    text += "  Q  a12();\n";
    text += "  Q  a22 = Q();\n";
    text += "  Q *a32 = new Q();\n";
    text += "}\n";

    LockedTopDUContext top = parse(text, DumpNone);

    QCOMPARE(top->childContexts().size(), 4);
    QCOMPARE(top->childContexts()[0]->localDeclarations().size(), 2);

    // Q(int var)
    Declaration *ctorDecl = top->childContexts()[0]->localDeclarations()[0];
    QCOMPARE(ctorDecl->uses().size(), 1);
    QList<RangeInRevision> uses = ctorDecl->uses().values().first();

    QCOMPARE(uses.size(), 5);
    QCOMPARE(uses[0], RangeInRevision(7, 16, 7, 17));
    QCOMPARE(uses[1], RangeInRevision(11, 7, 11, 8));
    QCOMPARE(uses[2], RangeInRevision(12, 11, 12, 12));
    QCOMPARE(uses[3], RangeInRevision(13, 15, 13, 16));
    QCOMPARE(uses[4], RangeInRevision(14, 7, 14, 8));

    // Q()
    ctorDecl = top->childContexts()[0]->localDeclarations()[1];
    QCOMPARE(ctorDecl->uses().size(), 1);
    uses = ctorDecl->uses().values().first();

    QCOMPARE(uses.size(), 4);
    QCOMPARE(uses[0], RangeInRevision(8, 9, 8, 10));
    QCOMPARE(uses[1], RangeInRevision(15, 8, 15, 9));
    QCOMPARE(uses[2], RangeInRevision(16, 12, 16, 13));
    QCOMPARE(uses[3], RangeInRevision(17, 16, 17, 17));

  }
  {
    QByteArray text;
    text += "template<typename T>\n";
    text += "class Q {\n";
    text += "public:\n";
    text += "  Q(T var) { }\n";
    text += "  Q() { }\n";
    text += "};\n";
    text += "template<typename T>\n";
    text += "class B : public Q<T> {\n";
    text += "public:\n";
    text += "  B(T var) : Q<T>(var) { }\n";
    text += "  B() : Q<T>() { }\n";
    text += "};\n";
    text += "int main(int argc, char* argv[]) {\n";
    text += "  Q<int>  a1(123);\n";
    text += "  Q<int>  a2 = Q<int>(123);\n";
    text += "  Q<int> *a3 = new Q<int>(123);\n";
    text += "  Q<int>  a4(argc);\n";
    text += "  Q<int>  a12();\n";
    text += "  Q<int>  a22 = Q<int>();\n";
    text += "  Q<int> *a32 = new Q<int>();\n";
    text += "}\n";

    LockedTopDUContext top = parse(text, DumpNone);

    QCOMPARE(top->childContexts().size(), 6);
    // first one is the template context
    QCOMPARE(top->childContexts()[1]->type(), DUContext::Class);
    QCOMPARE(top->childContexts()[1]->localDeclarations().size(), 2);

    // Q(T var)
    Declaration *ctorDecl = top->childContexts()[1]->localDeclarations()[0];
    QEXPECT_FAIL("", "no uses get reported for ctors of template classes", Abort);
    QCOMPARE(ctorDecl->uses().size(), 1);
    ///TODO
    /*
    QList<RangeInRevision> uses = ctorDecl->uses().values().first();

    QCOMPARE(uses.size(), 5);
    QCOMPARE(uses[0], RangeInRevision(7, 16, 7, 17));
    QCOMPARE(uses[1], RangeInRevision(11, 7, 11, 8));
    QCOMPARE(uses[2], RangeInRevision(12, 11, 12, 12));
    QCOMPARE(uses[3], RangeInRevision(13, 15, 13, 16));
    QCOMPARE(uses[4], RangeInRevision(14, 7, 14, 8));

    // Q()
    ctorDecl = top->childContexts()[1]->localDeclarations()[1];
    QCOMPARE(ctorDecl->uses().size(), 1);
    uses = ctorDecl->uses().values().first();

    QCOMPARE(uses.size(), 4);
    QCOMPARE(uses[0], RangeInRevision(8, 9, 8, 10));
    QCOMPARE(uses[1], RangeInRevision(15, 8, 15, 9));
    QCOMPARE(uses[2], RangeInRevision(16, 12, 16, 13));
    QCOMPARE(uses[3], RangeInRevision(17, 16, 17, 17));
    */
  }
}

void TestDUChain::testCodeModel() {
  QByteArray text("class C{}; void test() {}; ");
  LockedTopDUContext top = parse(text, DumpNone);

  uint itemCount;
  const CodeModelItem* items;
  CodeModel::self().items( top->url(), itemCount, items );

  uint validCount = 0;
  for(uint a = 0; a < itemCount; ++a) {
    if(items[a].id.isValid()) {
      if(items[a].id == QualifiedIdentifier("C"))
        QVERIFY(items[a].kind == CodeModelItem::Class);
      if(items[a].id == QualifiedIdentifier("test"))
        QVERIFY(items[a].kind == CodeModelItem::Function);
      ++validCount;
    }
  }

}

void TestDUChain::testDoWhile() {
  QByteArray text("void test() { int i; do { i = 2; i -= 3; } while(1); ");
  LockedTopDUContext top = parse(text, DumpNone);

  QCOMPARE(top->childContexts().count(), 2);
  QCOMPARE(top->childContexts()[1]->childContexts().count(), 2);
  kDebug() << top->childContexts()[1]->childContexts()[0] << top->childContexts()[1] << top;
  QCOMPARE(top->childContexts()[1]->childContexts()[0]->usesCount(), 2);

}

void TestDUChain::testEnumOverride() {
  QByteArray text("class B{class BA{};};class A{enum {B}; B::BA ba; };");
  LockedTopDUContext top = parse(text, DumpNone);

  QCOMPARE(top->childContexts().size(), 2);
  QCOMPARE(top->childContexts()[0]->localDeclarations().size(), 1);
  QCOMPARE(top->childContexts()[1]->localDeclarations().size(), 2);
  QCOMPARE(top->childContexts()[0]->localDeclarations()[0]->indexedType(), top->childContexts()[1]->localDeclarations()[1]->indexedType());

}

//Makes sure constructores cannot shadow the class itself
void TestDUChain::testDeclareSubClass() {
  QByteArray text("class Enclosing { class Class {Class(); class SubClass; class Frog; }; class Beach; }; class Enclosing::Class::SubClass { Class c; Frog f; Beach b; };");
  LockedTopDUContext top = parse(text, DumpNone);

  QCOMPARE(top->childContexts().count(), 2);
  QCOMPARE(top->localDeclarations().count(), 1);
  QCOMPARE(top->childContexts()[0]->localDeclarations().count(), 2);
  QCOMPARE(top->childContexts()[0]->childContexts().count(), 1);
  QCOMPARE(top->childContexts()[0]->childContexts()[0]->localDeclarations().count(), 3);
  QCOMPARE(top->childContexts()[1]->localDeclarations().count(), 1);
  QCOMPARE(top->childContexts()[1]->childContexts().count(), 1);
  QCOMPARE(top->childContexts()[1]->childContexts()[0]->localDeclarations().count(), 3); //A virtual context is placed around SubClass

  QCOMPARE(top->childContexts()[0]->childContexts()[0]->scopeIdentifier(true), QualifiedIdentifier("Enclosing::Class"));
  QVERIFY(top->childContexts()[0]->childContexts()[0]->inSymbolTable());

  //Verify that "Class c" is matched correctly
  QVERIFY(top->childContexts()[1]->childContexts()[0]->localDeclarations()[0]->abstractType().unsafeData());
  QVERIFY(!top->childContexts()[1]->childContexts()[0]->localDeclarations()[0]->type<DelayedType>().unsafeData());
  QCOMPARE(top->childContexts()[1]->childContexts()[0]->localDeclarations()[0]->abstractType()->indexed(), top->childContexts()[0]->localDeclarations()[0]->abstractType()->indexed());

  //Verify that Frog is found
  QVERIFY(top->childContexts()[1]->childContexts()[0]->localDeclarations()[1]->abstractType().unsafeData());
  QVERIFY(!top->childContexts()[1]->childContexts()[0]->localDeclarations()[1]->type<DelayedType>().unsafeData());
  QCOMPARE(top->childContexts()[1]->childContexts()[0]->localDeclarations()[1]->abstractType()->indexed(), top->childContexts()[0]->childContexts()[0]->localDeclarations()[2]->abstractType()->indexed());

  //Make sure Beach is found
  QVERIFY(top->childContexts()[1]->childContexts()[0]->localDeclarations()[2]->abstractType().unsafeData());
  QVERIFY(!top->childContexts()[1]->childContexts()[0]->localDeclarations()[2]->type<DelayedType>().unsafeData());
  QCOMPARE(top->childContexts()[1]->childContexts()[0]->localDeclarations()[2]->abstractType()->indexed(), top->childContexts()[0]->localDeclarations()[1]->abstractType()->indexed());

}

void TestDUChain::testBaseClasses() {
  QByteArray text("class A{int aValue; }; class B{int bValue;}; class C : public A{int cValue;}; class D : public A, B {int dValue;}; template<class Base> class F : public Base { int fValue;};");

  LockedTopDUContext top = parse(text, DumpNone);

  QCOMPARE(top->localDeclarations().count(), 5);
  Declaration* defClassA = top->localDeclarations().first();
  QCOMPARE(defClassA->identifier(), Identifier("A"));
  QVERIFY(defClassA->type<CppClassType>());

  Declaration* defClassB = top->localDeclarations()[1];
  QCOMPARE(defClassB->identifier(), Identifier("B"));
  QVERIFY(defClassB->type<CppClassType>());

  ClassDeclaration* defClassC = dynamic_cast<ClassDeclaration*>(top->localDeclarations()[2]);
  QVERIFY(defClassC);
  QCOMPARE(defClassC->identifier(), Identifier("C"));
  QVERIFY(defClassC->type<CppClassType>());
  QCOMPARE( defClassC->baseClassesSize(), 1u );

  ClassDeclaration* defClassD = dynamic_cast<ClassDeclaration*>(top->localDeclarations()[3]);
  QVERIFY(defClassD);
  QCOMPARE(defClassD->identifier(), Identifier("D"));
  QVERIFY(defClassD->type<CppClassType>());
  QCOMPARE( defClassD->baseClassesSize(), 2u );

  QVERIFY( findDeclaration( defClassD->internalContext(), Identifier("dValue") ) );
  QVERIFY( !findDeclaration( defClassD->internalContext(), Identifier("cValue") ) );
  QVERIFY( findDeclaration( defClassD->internalContext(), Identifier("aValue") ) );
  QVERIFY( findDeclaration( defClassD->internalContext(), Identifier("bValue") ) );

  QVERIFY( !findDeclaration( defClassC->internalContext(), Identifier("dValue") ) );
  QVERIFY( findDeclaration( defClassC->internalContext(), Identifier("cValue") ) );
  QVERIFY( !findDeclaration( defClassC->internalContext(), Identifier("bValue") ) );
  QVERIFY( findDeclaration( defClassC->internalContext(), Identifier("aValue") ) );

  QVERIFY( !findDeclaration( defClassB->internalContext(), Identifier("dValue") ) );
  QVERIFY( !findDeclaration( defClassB->internalContext(), Identifier("cValue") ) );
  QVERIFY( findDeclaration( defClassB->internalContext(), Identifier("bValue") ) );
  QVERIFY( !findDeclaration( defClassB->internalContext(), Identifier("aValue") ) );

  QVERIFY( !findDeclaration( defClassA->internalContext(), Identifier("dValue") ) );
  QVERIFY( !findDeclaration( defClassA->internalContext(), Identifier("cValue") ) );
  QVERIFY( !findDeclaration( defClassA->internalContext(), Identifier("bValue") ) );
  QVERIFY( findDeclaration( defClassA->internalContext(), Identifier("aValue") ) );

  ///Now test a template-class as base-class
  ClassDeclaration* defClassF = dynamic_cast<ClassDeclaration*>(top->localDeclarations()[4]);
  QVERIFY(defClassF);
  QCOMPARE(defClassF->identifier(), Identifier("F"));
  QVERIFY(defClassF->type<CppClassType>());
  QCOMPARE( defClassF->baseClassesSize(), 1u );
  QVERIFY( defClassF->baseClasses()[0].baseClass.type<DelayedType>().unsafeData() );

  Declaration* FDDecl = findDeclaration(top, QualifiedIdentifier("F<D>") );
  QVERIFY(FDDecl);
  QVERIFY(FDDecl->internalContext() != defClassF->internalContext());

  QVERIFY( findDeclaration( FDDecl->internalContext(), Identifier("dValue") ) );
  QVERIFY( !findDeclaration( FDDecl->internalContext(), Identifier("cValue") ) );
  QVERIFY( findDeclaration( FDDecl->internalContext(), Identifier("aValue") ) );
  QVERIFY( findDeclaration( FDDecl->internalContext(), Identifier("bValue") ) );
  QVERIFY( findDeclaration( FDDecl->internalContext(), Identifier("fValue") ) );

}

void TestDUChain::testTypedefUnsignedInt() {
  QByteArray method("typedef long unsigned int MyInt; MyInt v;");

  LockedTopDUContext top = parse(method, DumpNone);

  QCOMPARE(top->localDeclarations().count(), 2);
  QVERIFY(top->localDeclarations()[0]->abstractType());
  QVERIFY(top->localDeclarations()[1]->abstractType());
  QCOMPARE(top->localDeclarations()[0]->abstractType()->toString(), QString("MyInt"));
  QCOMPARE(top->localDeclarations()[1]->abstractType()->toString(), QString("MyInt"));
  QCOMPARE(unAliasedType(top->localDeclarations()[0]->abstractType())->toString(), QString("long unsigned int"));
  QCOMPARE(unAliasedType(top->localDeclarations()[1]->abstractType())->toString(), QString("long unsigned int"));
}

void TestDUChain::testTypedef() {
  QByteArray method("/*This is A translation-unit*/ \n/*This is class A*/class A { }; \ntypedef A B;//This is a typedef\nvoid test() { };\nconst B c;");

  LockedTopDUContext top = parse(method, DumpNone);

  QCOMPARE(top->localDeclarations().count(), 4);

  Declaration* defClassA = top->localDeclarations().first();
  QCOMPARE(defClassA->identifier(), Identifier("A"));
  QVERIFY(defClassA->type<CppClassType>());
  QCOMPARE(QString::fromUtf8(defClassA->comment()), QString("This is class A"));

  DUContext* classA = top->childContexts().first();
  QVERIFY(classA->parentContext());
  QCOMPARE(classA->importedParentContexts().count(), 0);
  QCOMPARE(classA->localScopeIdentifier(), QualifiedIdentifier("A"));

  Declaration* defB = findDeclaration(top,  Identifier("B"));
  QVERIFY(defB->indexedType() != defClassA->indexedType());
  QVERIFY(defB->isTypeAlias());
  TypeAliasType::Ptr aliasType = defB->type<TypeAliasType>();
  QVERIFY(aliasType);
  QVERIFY(aliasType->type());
  QCOMPARE(aliasType->type()->indexed(),  defClassA->indexedType());
  QCOMPARE(aliasType->declaration(top), defB);
  QCOMPARE(defB->kind(), Declaration::Type);
  QCOMPARE(QString::fromUtf8(defB->comment()), QString("This is a typedef"));

  QCOMPARE(defB->logicalInternalContext(top), defClassA->logicalInternalContext(top));

  Declaration* defC = findDeclaration(top, QualifiedIdentifier("c"));
  QVERIFY(defC);
  //The "const" has to be moved into the pointed-to type so it is correctly respected during type-conversions and such
  QVERIFY(TypeUtils::unAliasedType(defC->abstractType()));
  QVERIFY(TypeUtils::unAliasedType(defC->abstractType())->modifiers() & AbstractType::ConstModifier);
  QVERIFY(defC->abstractType()->modifiers() & AbstractType::ConstModifier);

}

void TestDUChain::testTypedefFuncptr()
{
  QByteArray method("typedef int (*func)(char c); func f('c');");

  LockedTopDUContext top = parse(method, DumpAll);

  QCOMPARE(top->localDeclarations().count(), 2);
  QVERIFY(top->localDeclarations()[0]->abstractType());
  QVERIFY(top->localDeclarations()[1]->abstractType());
  QCOMPARE(top->localDeclarations()[0]->abstractType()->toString(), QString("func"));
  QCOMPARE(top->localDeclarations()[1]->abstractType()->toString(), QString("func"));
  QCOMPARE(unAliasedType(top->localDeclarations()[0]->abstractType())->toString(), QString("function int (char)"));
  QCOMPARE(unAliasedType(top->localDeclarations()[1]->abstractType())->toString(), QString("function int (char)"));

  AbstractType::Ptr target = TypeUtils::targetTypeKeepAliases( top->localDeclarations()[1]->abstractType(), top);
  const IdentifiedType* idType = dynamic_cast<const IdentifiedType*>( target.unsafeData() );
  QVERIFY(idType);
  QVERIFY(idType->declaration(top));

  QCOMPARE(top->childContexts().at(0)->localDeclarations().count(), 1);
  Declaration* dec = top->childContexts().at(0)->localDeclarations().first();
  QCOMPARE(dec->toString(), QString("char c"));
}

void TestDUChain::testContextAssignment() {
  QByteArray text("template<class A>class Class { enum{ Bla = A::a }; }; ");
  LockedTopDUContext top = parse(text, DumpNone);

  QCOMPARE(top->localDeclarations().count(), 1);

  QCOMPARE(top->childContexts().count(), 2);
  QCOMPARE(top->childContexts()[1]->owner(), top->localDeclarations()[0]);

  QCOMPARE(top->childContexts()[1]->localDeclarations().count(), 1);
  QCOMPARE(top->childContexts()[1]->childContexts().count(), 1);

  QCOMPARE((void*)top->childContexts()[0]->owner(), (void*)0);
  QVERIFY((void*)top->childContexts()[1]->localDeclarations()[0]->internalContext());
  QCOMPARE((void*)top->childContexts()[1]->localDeclarations()[0]->internalContext(), top->childContexts()[1]->childContexts()[0]);

}

void TestDUChain::testSpecializedTemplates() {
  {
    QByteArray text("struct C{}; template<class T>class AsPointer {typedef T* Ptr;}; template<class T>class AsPointer<T*> {typedef T* Ptr;}; template<class T>class AsPointer<const T*> {typedef T* Ptr2;}; template<class T>class AsPointer<const T> {typedef T* Ptr2;};");
    LockedTopDUContext top = parse(text, DumpNone);

    QCOMPARE(top->localDeclarations().count(), 5);
    TemplateDeclaration* base = dynamic_cast<TemplateDeclaration*>(top->localDeclarations()[1]);
    QVERIFY(base);
    QCOMPARE(base->specializationsSize(), 3u);

    TemplateDeclaration* sp3AsTemplate = dynamic_cast<TemplateDeclaration*>(top->localDeclarations()[3]);
    //Must be const T*
    QVERIFY(sp3AsTemplate);
    QCOMPARE(sp3AsTemplate->specializedWith().information().templateParametersSize(), 1u);
    kDebug() << "specialized with" << sp3AsTemplate->specializedWith().information().templateParameters()[0].abstractType()->toString();
    QVERIFY(sp3AsTemplate->specializedWith().information().templateParameters()[0].abstractType());
    QVERIFY(sp3AsTemplate->specializedWith().information().templateParameters()[0].type<PointerType>());
    QVERIFY(! (sp3AsTemplate->specializedWith().information().templateParameters()[0].abstractType()->modifiers() & AbstractType::ConstModifier) );
    QVERIFY(sp3AsTemplate->specializedWith().information().templateParameters()[0].type<PointerType>()->baseType());
    QVERIFY(sp3AsTemplate->specializedWith().information().templateParameters()[0].type<PointerType>()->baseType().cast<DelayedType>());
    QVERIFY(sp3AsTemplate->specializedWith().information().templateParameters()[0].type<PointerType>()->baseType().cast<DelayedType>()->identifier().isConstant());



    Declaration* decl2 = findDeclaration(top, QualifiedIdentifier("AsPointer<const C>::Ptr2"));
    Declaration* decl1 = findDeclaration(top, QualifiedIdentifier("AsPointer<C>::Ptr"));
    Declaration* decl3 = findDeclaration(top, QualifiedIdentifier("AsPointer<C*>::Ptr"));
    Declaration* decl4 = findDeclaration(top, QualifiedIdentifier("AsPointer<const C*>::Ptr2"));

    QVERIFY(decl1);
    QVERIFY(decl2);
    QVERIFY(decl3);
    QVERIFY(decl4);

    QVERIFY(unAliasedType(decl1->abstractType()).cast<PointerType>());
    QCOMPARE(unAliasedType(decl1->abstractType())->indexed(), unAliasedType(decl2->abstractType())->indexed());
    QCOMPARE(unAliasedType(decl1->abstractType())->indexed(), unAliasedType(decl3->abstractType())->indexed());
    QCOMPARE(unAliasedType(decl1->abstractType())->indexed(), unAliasedType(decl4->abstractType())->indexed());

  }
  {
    QByteArray text("class A{}; class B{}; class C{}; template<class T,class T2> class E{typedef A Type1;}; template<class T2> class E<A,T2> { typedef B Type2; typedef T2 NotA; }; template<class T2> class E<T2,A> { typedef C Type3; typedef T2 NotA; };");
    LockedTopDUContext top = parse(text, DumpNone);

    QCOMPARE(top->localDeclarations().count(), 6);

    Declaration* EDecl = top->localDeclarations()[3];
    Declaration* E1Decl = top->localDeclarations()[4];
    Declaration* E2Decl = top->localDeclarations()[5];

    QVERIFY(EDecl->internalContext());
    QVERIFY(EDecl->internalContext()->importedParentContexts().count());
    QVERIFY(EDecl->internalContext()->importedParentContexts()[0].context(top)->type() == DUContext::Template);
    QCOMPARE(EDecl->internalContext()->importedParentContexts()[0].context(top)->localDeclarations().count(), 2);

    QVERIFY(E1Decl->internalContext());
    QCOMPARE(E1Decl->internalContext()->importedParentContexts().count(), 1);
    QCOMPARE(E1Decl->internalContext()->importedParentContexts()[0].context(top)->type(), DUContext::Template);
    QCOMPARE(E1Decl->internalContext()->importedParentContexts()[0].context(top)->localDeclarations().count(), 1);
    QCOMPARE(E1Decl->internalContext()->localDeclarations().count(), 2);
    QCOMPARE(E1Decl->identifier().templateIdentifiersCount(), 2u);
    QVERIFY(E2Decl->internalContext());
    QCOMPARE(E2Decl->internalContext()->importedParentContexts().count(), 1);
    QCOMPARE(E2Decl->internalContext()->importedParentContexts()[0].context(top)->type(), DUContext::Template);
    QCOMPARE(E2Decl->internalContext()->importedParentContexts()[0].context(top)->localDeclarations().count(), 1);
    QCOMPARE(E2Decl->identifier().templateIdentifiersCount(), 2u);

    TemplateDeclaration* templateEDecl = dynamic_cast<TemplateDeclaration*>(EDecl);
    TemplateDeclaration* templateE1Decl = dynamic_cast<TemplateDeclaration*>(E1Decl);
    TemplateDeclaration* templateE2Decl = dynamic_cast<TemplateDeclaration*>(E2Decl);
    kDebug() << E1Decl->identifier().toString();
    QVERIFY(templateEDecl);
    QVERIFY(templateE1Decl);
    QVERIFY(templateE2Decl);
    QCOMPARE(templateE1Decl->specializedWith().information().templateParametersSize(), 2u);
    QCOMPARE(templateE2Decl->specializedWith().information().templateParametersSize(), 2u);
    QVERIFY(!templateE1Decl->specializedWith().information().templateParameters()[0].type<DelayedType>());
    kDebug() << typeid(*templateE1Decl->specializedWith().information().templateParameters()[1].abstractType()).name();
    QVERIFY(templateE1Decl->specializedWith().information().templateParameters()[1].type<DelayedType>());
    QVERIFY(templateE2Decl->specializedWith().information().templateParameters()[0].type<DelayedType>());
    QVERIFY(!templateE2Decl->specializedWith().information().templateParameters()[1].type<DelayedType>());

    QCOMPARE(dynamic_cast<TemplateDeclaration*>(templateE1Decl->specializedFrom().data()), templateEDecl);
    QCOMPARE(dynamic_cast<TemplateDeclaration*>(templateE2Decl->specializedFrom().data()), templateEDecl);

    Declaration* foundE1Specialization = findDeclaration(top, QualifiedIdentifier("E<A,C>::Type2"));
    QVERIFY(foundE1Specialization);
    Declaration* foundE1Specialization2 = findDeclaration(top, QualifiedIdentifier("E<A,C>::NotA"));
    QVERIFY(foundE1Specialization2);
    QCOMPARE(unAliasedType(foundE1Specialization2->abstractType())->indexed(), top->localDeclarations()[2]->indexedType());

    Declaration* foundE2Specialization = findDeclaration(top, QualifiedIdentifier("E<C,A>::Type3"));
    QVERIFY(foundE2Specialization);
    Declaration* foundE2Specialization2 = findDeclaration(top, QualifiedIdentifier("E<C,A>::NotA"));
    QVERIFY(foundE2Specialization2);
    QCOMPARE(unAliasedType(foundE2Specialization2->abstractType())->indexed(), top->localDeclarations()[2]->indexedType());

    QVERIFY(findDeclaration(top, QualifiedIdentifier("E<C,A>")));
    QCOMPARE(findDeclaration(top, QualifiedIdentifier("E<C,A>"))->identifier(), Identifier("E<C,A>"));

  }
  {
    // Test template specializations of nested classes
    QByteArray text("struct A { struct B { template<class T> struct C {}; }; };\n"
                    "template<> struct A::B::C<int> { typedef int Type; };\n");
    LockedTopDUContext top = parse(text, DumpNone);

    Declaration* specialization = findDeclaration(top, QualifiedIdentifier("A::B::C<int>::Type"));
    QVERIFY(specialization);
  }
  {
    //                 0         1         2         3         4         5
    //                 012345678901234567890123456789012345678901234567890123456789
    QByteArray text("template<class T>\n"
                    "class A { void foo(T arg); };\n"
                    "template<class T> void A<T>::foo(T arg) {}");
    LockedTopDUContext top = parse(text, DumpNone);

    QCOMPARE(top->localDeclarations().count(), 2);
    TemplateDeclaration* base = dynamic_cast<TemplateDeclaration*>(top->localDeclarations()[0]);
    QVERIFY(base);
    QCOMPARE(base->specializationsSize(), 0u);
    QCOMPARE(base->instantiations().size(), 1);
    QCOMPARE(base->instantiations().begin().value()->specializationsSize(), 0u);
    // use of class T in A<T>
    QCOMPARE(top->childContexts().size(), 5);
    QCOMPARE(top->childContexts().at(2)->type(), DUContext::Template);
    QCOMPARE(top->childContexts().at(2)->localDeclarations().size(), 1);
    QEXPECT_FAIL("", "The uses of T are not reported when we define the default implementation outside the class body", Abort);
    QCOMPARE(top->childContexts().at(2)->localDeclarations().first()->uses().size(), 1);
    QCOMPARE(top->childContexts().at(2)->localDeclarations().first()->uses().begin()->size(), 2);
    QCOMPARE(top->childContexts().at(2)->localDeclarations().first()->uses().begin()->at(0), RangeInRevision(2, 22, 2, 23));
    QCOMPARE(top->childContexts().at(2)->localDeclarations().first()->uses().begin()->at(1), RangeInRevision(2, 31, 2, 32));
  }
}

int value( const AbstractType::Ptr& type ) {
  const ConstantIntegralType* integral = dynamic_cast<const ConstantIntegralType*>(type.unsafeData());
  if( integral )
    return (int)integral->value<qint64>();
  else
    return 0;
}

void TestDUChain::testTemplateRecursiveInstantiation()
{
  {
    QByteArray text("template<bool b> class A { public: bool member; enum { SizeWithFalse = sizeof(A<false>) }; };");
    LockedTopDUContext top = parse(text, DumpNone);

    Declaration* aTrueDecl = findDeclaration(top, Identifier("A<true>"));
    Declaration* aFalseDecl = findDeclaration(top, Identifier("A<false>"));
    QVERIFY(aTrueDecl);
    QVERIFY(aFalseDecl);
    QVERIFY(aTrueDecl->internalContext());
    QVERIFY(aFalseDecl->internalContext());

    QCOMPARE(aFalseDecl->internalContext()->localDeclarations(top).count(), 2);
    QCOMPARE(aTrueDecl->internalContext()->localDeclarations(top).count(), 2);

  }
}

void TestDUChain::testTemplateEnums()
{
  {
    QByteArray text("template<bool num> struct No {};  No<true> n;");
    LockedTopDUContext top = parse(text, DumpNone);

    QCOMPARE(top->localDeclarations().count(), 2);
    QVERIFY(top->localDeclarations()[1]->abstractType());
    QCOMPARE(top->localDeclarations()[1]->abstractType()->toString(), QString("No< true >"));

  }
  {
    QByteArray text("template<int num=5> struct No {};  No<> n;");
    LockedTopDUContext top = parse(text, DumpNone);

    QCOMPARE(top->localDeclarations().count(), 2);
    QVERIFY(top->localDeclarations()[1]->abstractType());
    QCOMPARE(top->localDeclarations()[1]->abstractType()->toString(), QString("No< 5 >"));
    QCOMPARE(top->childContexts().count(), 2);
    QCOMPARE(top->childContexts()[0]->localDeclarations().count(), 1);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[0]->kind(), Declaration::Instance);


    QCOMPARE(top->usesCount(), 1);
    Declaration* used = top->usedDeclarationForIndex(top->uses()[0].m_declarationIndex);
    QVERIFY(used);
    QVERIFY(used->abstractType());
    QCOMPARE(used->abstractType()->toString(), QString("No< 5 >"));
  }
  {
    QByteArray text("template<int num> struct No {};  No<9> n;");
    LockedTopDUContext top = parse(text, DumpNone);

    QCOMPARE(top->localDeclarations().count(), 2);
    QVERIFY(top->localDeclarations()[1]->abstractType());
    QCOMPARE(top->localDeclarations()[1]->abstractType()->toString(), QString("No< 9 >"));

    QCOMPARE(top->usesCount(), 1);
    Declaration* used = top->usedDeclarationForIndex(top->uses()[0].m_declarationIndex);
    QVERIFY(used);
    QVERIFY(used->abstractType());
    QCOMPARE(used->abstractType()->toString(), QString("No< 9 >"));
  }
  {
    QByteArray text("class A {enum { Val = 5}; }; class B { enum{ Val = 7 }; }; template<class C, int i> class Test { enum { TempVal = C::Val, Num = i, Sum = TempVal + i }; };");
    LockedTopDUContext top = parse(text, DumpNone);

    QCOMPARE(top->localDeclarations().count(), 3);
    Declaration* testDecl = top->localDeclarations()[2];

    TemplateDeclaration* templateTestDecl = dynamic_cast<TemplateDeclaration*>(testDecl);
    QVERIFY(templateTestDecl);

    Declaration* tempDecl = findDeclaration( top, QualifiedIdentifier("Test<A, 3>::TempVal") );
    QVERIFY(tempDecl);
    AbstractType::Ptr t = tempDecl->abstractType();
    QVERIFY(!DelayedType::Ptr::dynamicCast(t));
    QVERIFY(ConstantIntegralType::Ptr::dynamicCast(t));
    QCOMPARE(value(tempDecl->abstractType()), 5);

    tempDecl = findDeclaration( top, QualifiedIdentifier("Test<A, 3>::Num") );
    QVERIFY(tempDecl);
    QCOMPARE(value(tempDecl->abstractType()), 3);

    tempDecl = findDeclaration( top, QualifiedIdentifier("Test<A, 3>::Sum") );
    QVERIFY(tempDecl->abstractType());
    QCOMPARE(value(tempDecl->abstractType()), 8);

    tempDecl = findDeclaration( top, QualifiedIdentifier("Test<B, 9>::TempVal") );
    QVERIFY(tempDecl->abstractType());
    QCOMPARE(value(tempDecl->abstractType()), 7);

    tempDecl = findDeclaration( top, QualifiedIdentifier("Test<B, 9>::Num") );
    QVERIFY(tempDecl->abstractType());
    t = tempDecl->abstractType();
    kDebug() << "id" << typeid(*t).name();
    kDebug() << "text:" << tempDecl->abstractType()->toString() << tempDecl->toString();
    QCOMPARE(value(tempDecl->abstractType()), 9);

    tempDecl = findDeclaration( top, QualifiedIdentifier("Test<B, 9>::Sum") );
    QVERIFY(tempDecl->abstractType());
    QCOMPARE(value(tempDecl->abstractType()), 16);

  }
}

void TestDUChain::testIntegralTemplates()
{
  QByteArray text("template<class T> class A { T i; }; ");
  LockedTopDUContext top = parse(text, DumpNone);

  QCOMPARE(top->localDeclarations().count(), 1);
  QCOMPARE(top->childContexts().count(), 2);

  QCOMPARE(top->childContexts()[1]->localDeclarations().count(), 1);
  Declaration* dec = findDeclaration( top, QualifiedIdentifier( "A<int>::i") );
  QVERIFY(dec);
  AbstractType::Ptr t = dec->abstractType();
  IntegralType* integral = dynamic_cast<IntegralType*>( t.unsafeData() );
  QVERIFY( integral );
  QCOMPARE( integral->dataType(), (uint)IntegralType::TypeInt );

  QCOMPARE(top->childContexts()[1]->localDeclarations().count(), 1);
  dec = findDeclaration( top, QualifiedIdentifier( "A<unsigned int>::i") );
  t = dec->abstractType();
  integral = dynamic_cast<IntegralType*>( t.unsafeData() );
  QVERIFY( integral );
  QCOMPARE( integral->dataType(), (uint)IntegralType::TypeInt );
  QCOMPARE( integral->modifiers(), (unsigned long long)AbstractType::UnsignedModifier );

  QCOMPARE(top->childContexts()[1]->localDeclarations().count(), 1);
  dec = findDeclaration( top, QualifiedIdentifier( "A<long double>::i") );
  t = dec->abstractType();
  integral = dynamic_cast<IntegralType*>( t.unsafeData() );
  QVERIFY( integral );
  QCOMPARE( integral->dataType(), (uint)IntegralType::TypeDouble );
  QCOMPARE( integral->modifiers(), (unsigned long long)AbstractType::LongModifier );

}

void TestDUChain::testFunctionTemplates() {
  QByteArray method("template<class T> T test(const T& t) {};");

  LockedTopDUContext top = parse(method, DumpNone);

  QCOMPARE(top->childContexts().size(), 3);
  Declaration* defTest = top->localDeclarations()[0];
  QVERIFY(top->childContexts()[0]->type() == DUContext::Template);
  QCOMPARE(top->childContexts()[0]->importers().size(), 1);
  kDebug() << top->childContexts()[0]->importers()[0] << top->childContexts()[1] << top->childContexts()[2];
  QCOMPARE(top->childContexts()[0]->importers()[0], top->childContexts()[1]);
  QCOMPARE(defTest->identifier(), Identifier("test"));
  QVERIFY(defTest->type<FunctionType>());
  QVERIFY( isTemplateDeclaration(defTest) );
  QVERIFY(defTest->internalContext());
  QCOMPARE(defTest->internalContext()->importedParentContexts().size(), 1);
  KDevelop::DUContext* tempCtx = getTemplateContext(defTest);
  QVERIFY(tempCtx);

  QCOMPARE( defTest->type<FunctionType>()->arguments().count(), 1 );
  QVERIFY( realType(defTest->type<FunctionType>()->arguments()[0], 0).cast<DelayedType>() );

}

void TestDUChain::testTemplateFunctions() {
  QByteArray method("class A {}; template<class T> T a(T& q) {};template<class T> struct TC { void test(const T&); };");

  LockedTopDUContext top = parse(method, DumpNone);
  parse(method, DumpNone, top);

  QCOMPARE(top->localDeclarations().count(), 3);
  Declaration* d = findDeclaration(top, QualifiedIdentifier("a<A>"));
  QVERIFY(d);
  FunctionType::Ptr cppFunction = d->type<FunctionType>();
  QVERIFY(cppFunction);
  QCOMPARE(cppFunction->arguments().count(), 1);
  QCOMPARE(cppFunction->returnType()->indexed(), top->localDeclarations()[0]->abstractType()->indexed());
  QCOMPARE(cppFunction->arguments()[0]->toString(), QString("A&"));
  QVERIFY(d->internalContext());
  QVERIFY(d->internalContext()->type() == DUContext::Other);
  QCOMPARE(d->internalContext()->importedParentContexts().count(), 1);
  QCOMPARE(d->internalContext()->importedParentContexts()[0].context(0)->type(), DUContext::Function);
  QCOMPARE(d->internalContext()->importedParentContexts()[0].context(0)->importedParentContexts().count(), 1);
  QCOMPARE(d->internalContext()->importedParentContexts()[0].context(0)->importedParentContexts().count(), 1);
  QCOMPARE(d->internalContext()->importedParentContexts()[0].context(0)->importedParentContexts()[0].context(0)->type(), DUContext::Template);

  QList<QPair<Declaration*, int> > visibleDecls = d->internalContext()->allDeclarations(d->internalContext()->range().end, top, false);
  for(int a = 0; a < visibleDecls.size(); ++a) {
    kDebug() << "decl:" << visibleDecls[a].first->toString();
  }
  QCOMPARE(visibleDecls.size(), 2); //Must be q and T
  QCOMPARE(visibleDecls[0].first->identifier().toString(), QString("q"));
  QVERIFY(visibleDecls[0].first->abstractType());
  QCOMPARE(visibleDecls[0].first->abstractType()->toString(), QString("A&"));
  QVERIFY(visibleDecls[1].first->abstractType());
  QCOMPARE(visibleDecls[1].first->abstractType()->toString(), QString("A"));

  Declaration* found = findDeclaration(d->internalContext(), Identifier("q"));
  QVERIFY(found);
  QVERIFY(found->abstractType());
  QCOMPARE(found->abstractType()->toString(), QString("A&"));

  Declaration* instTC = findDeclaration(top, QualifiedIdentifier("TC<int>"));
  QVERIFY(instTC);
  QVERIFY(instTC->internalContext());
  QList<Declaration*> decls = instTC->internalContext()->findLocalDeclarations(Identifier("test"));
  QCOMPARE(decls.count(), 1);
  Declaration* testDecl = decls.first();
  QVERIFY(testDecl);
  QVERIFY(testDecl->abstractType());
  QVERIFY(testDecl->internalContext());
  QVERIFY(testDecl->type<KDevelop::FunctionType>());
  QCOMPARE(testDecl->internalContext()->localDeclarations(top).count(), 1);
  kDebug() << testDecl->abstractType()->toString();
  QCOMPARE(testDecl->type<KDevelop::FunctionType>()->arguments().count(), 1);

  DUContext* argContext = KDevelop::DUChainUtils::getArgumentContext(testDecl);
  QVERIFY(argContext);
  QCOMPARE(argContext->type(), DUContext::Function);
  QCOMPARE(argContext->localDeclarations(top).count(), 1);


}

void TestDUChain::testTemplateDependentClass() {
  QByteArray method("class A {}; template<class T> class B { class Q{ typedef T Type; }; }; B<A>::Q::Type t;");

  LockedTopDUContext top = parse(method, DumpNone);

  Declaration* d = findDeclaration(top, QualifiedIdentifier("t"));
  QVERIFY(d);
  kDebug() << d->toString();
  QCOMPARE(unAliasedType(d->abstractType())->indexed(), top->localDeclarations()[0]->abstractType()->indexed());

}

void TestDUChain::testMetaProgramming() {
  QByteArray method("template<int value> class Factorial{ enum { Value = value * Factorial<value-1>::Value };}; template<> class Factorial<0> { enum { Value = 1 };};");

  LockedTopDUContext top = parse(method, DumpNone);

  QCOMPARE(top->localDeclarations().count(), 2);
  TemplateDeclaration* templateBase = dynamic_cast<TemplateDeclaration*>(top->localDeclarations()[0]);
  TemplateDeclaration* templateSpecialization = dynamic_cast<TemplateDeclaration*>(top->localDeclarations()[1]);
  QVERIFY(templateBase);
  QVERIFY(templateSpecialization);
  QVERIFY(templateBase->specializationsSize() == 1);
  QCOMPARE(templateSpecialization->specializedFrom().data(), top->localDeclarations()[0]);
  QCOMPARE(templateSpecialization->specializedWith().information().templateParametersSize(), 1u);
  QVERIFY(templateSpecialization->specializedWith().information().templateParameters()[0].abstractType());
  QVERIFY(templateSpecialization->specializedWith().information().templateParameters()[0].type<ConstantIntegralType>());
  QCOMPARE(templateSpecialization->specializedWith().information().templateParameters()[0].type<ConstantIntegralType>()->value<int>(), 0);
  kDebug() << "searching";
  Declaration* factorial0Container = findDeclaration(top, QualifiedIdentifier("Factorial<0>"));
  QCOMPARE(factorial0Container, top->localDeclarations()[1]);
  QVERIFY(factorial0Container);
  QCOMPARE(factorial0Container->internalContext()->childContexts().count(), 1);
  QCOMPARE(factorial0Container->internalContext()->childContexts()[0]->localDeclarations().count(), 1);
  QVERIFY(factorial0Container->internalContext()->childContexts()[0]->localDeclarations()[0]->type<ConstantIntegralType>());

  Declaration* factorial0 = findDeclaration(top, QualifiedIdentifier("Factorial<0>::Value"));
  QVERIFY(factorial0);
  QVERIFY(factorial0->type<ConstantIntegralType>());
  QCOMPARE(factorial0->type<ConstantIntegralType>()->value<int>(), 1);

  Declaration* factorial2 = findDeclaration(top, QualifiedIdentifier("Factorial<2>::Value"));
  QVERIFY(factorial2);
  QVERIFY(factorial2->type<ConstantIntegralType>());
  QCOMPARE(factorial2->type<ConstantIntegralType>()->value<int>(), 2);

  Declaration* factorial3 = findDeclaration(top, QualifiedIdentifier("Factorial<3>::Value"));
  QVERIFY(factorial3);
  QVERIFY(factorial3->type<ConstantIntegralType>());
  QCOMPARE(factorial3->type<ConstantIntegralType>()->value<int>(), 6);

  Declaration* factorial4 = findDeclaration(top, QualifiedIdentifier("Factorial<4>::Value"));
  QVERIFY(factorial4);
  QVERIFY(factorial4->type<ConstantIntegralType>());
  QCOMPARE(factorial4->type<ConstantIntegralType>()->value<int>(), 24);

}

void TestDUChain::testMetaProgramming3() {
  QByteArray method("template<int value, int than>class bigger_than {enum {Result = ((value > than) ? 2 : ((value == than) ? 0 : -2))};};");

  LockedTopDUContext top = parse(method, DumpNone);

  QCOMPARE(top->localDeclarations().count(), 1);

  kDebug() << "test 1";
  Declaration* decl = findDeclaration(top, QualifiedIdentifier("bigger_than<5, 3>::Result"));
  QVERIFY(decl);
  QVERIFY(decl->type<ConstantIntegralType>());
  QCOMPARE(decl->type<ConstantIntegralType>()->value<int>(), 2);
  kDebug() << "test 2";
  decl = findDeclaration(top, QualifiedIdentifier("bigger_than<5, 5>::Result"));
  QVERIFY(decl);
  QVERIFY(decl->type<ConstantIntegralType>());
  QCOMPARE(decl->type<ConstantIntegralType>()->value<int>(), 0);

  decl = findDeclaration(top, QualifiedIdentifier("bigger_than<5, 6>::Result"));
  QVERIFY(decl);
  QVERIFY(decl->type<ConstantIntegralType>());
  QCOMPARE(decl->type<ConstantIntegralType>()->value<int>(), -2);

}


void TestDUChain::testMetaProgramming2() {
  QByteArray method("template<int N, int R>class Permutations {public:enum { value = N*(Permutations<N-1,R-1>::value) };};template<int N>class Permutations<N, 0> {public:enum { value = 1 };};");

  LockedTopDUContext top = parse(method, DumpNone);

  QCOMPARE(top->localDeclarations().count(), 2);
  TemplateDeclaration* templateBase = dynamic_cast<TemplateDeclaration*>(top->localDeclarations()[0]);
  TemplateDeclaration* templateSpecialization = dynamic_cast<TemplateDeclaration*>(top->localDeclarations()[1]);
  QVERIFY(templateBase);
  QVERIFY(templateSpecialization);
  QVERIFY(templateBase->specializationsSize() == 1);
  QCOMPARE(templateSpecialization->specializedFrom().data(), top->localDeclarations()[0]);
  QCOMPARE(templateSpecialization->specializedWith().information().templateParametersSize(), 2u);
  QVERIFY(templateSpecialization->specializedWith().information().templateParameters()[1].abstractType());
  QVERIFY(templateSpecialization->specializedWith().information().templateParameters()[1].type<ConstantIntegralType>());
  QCOMPARE(templateSpecialization->specializedWith().information().templateParameters()[1].type<ConstantIntegralType>()->value<int>(), 0);

  Declaration* permutations0 = findDeclaration(top, QualifiedIdentifier("Permutations<5, 0>::value"));
  QVERIFY(permutations0);
  QVERIFY(permutations0->type<ConstantIntegralType>());
  QCOMPARE(permutations0->type<ConstantIntegralType>()->value<int>(), 1);

  Declaration* permutations2 = findDeclaration(top, QualifiedIdentifier("Permutations<2, 1>::value"));
  QVERIFY(permutations2);
  QVERIFY(permutations2->type<ConstantIntegralType>());
  QCOMPARE(permutations2->type<ConstantIntegralType>()->value<int>(), 2);

  Declaration* permutations3 = findDeclaration(top, QualifiedIdentifier("Permutations<4, 2>::value"));
  QVERIFY(permutations3);
  QVERIFY(permutations3->type<ConstantIntegralType>());
  QCOMPARE(permutations3->type<ConstantIntegralType>()->value<int>(), 12);

  Declaration* permutations4 = findDeclaration(top, QualifiedIdentifier("Permutations<10, 5>::value"));
  QVERIFY(permutations4);
  QVERIFY(permutations4->type<ConstantIntegralType>());
  kDebug() << permutations4->abstractType()->toString();
  QCOMPARE(permutations4->type<ConstantIntegralType>()->value<int>(), 30240);

}
void TestDUChain::testTemplateInternalSearch() {
  QByteArray method("class A {}; template<class T> class B { B mem(); const B mem2;}; ");

  LockedTopDUContext top = parse(method, DumpNone);

  QCOMPARE(top->childContexts().count(), 3);
  QCOMPARE(top->childContexts()[2]->localDeclarations().count(), 2);
  QVERIFY(top->childContexts()[2]->localDeclarations()[1]->type<DelayedType>());
  QVERIFY(top->childContexts()[2]->localDeclarations()[0]->type<FunctionType>());
  QVERIFY(top->childContexts()[2]->localDeclarations()[0]->type<FunctionType>()->returnType().cast<DelayedType>());

  Declaration* d = findDeclaration(top, QualifiedIdentifier("B<A>::mem"));
  QVERIFY(d);
  kDebug() << d->toString();
  FunctionType::Ptr fType = d->type<FunctionType>();
  QVERIFY(fType);
  QVERIFY(fType->returnType());
  kDebug() << fType->toString();
  QCOMPARE(fType->returnType()->toString(), QString("B< A >"));

  d = findDeclaration(top, QualifiedIdentifier("B<A>::mem2"));
  QVERIFY(d);
  QCOMPARE(d->abstractType()->toString(), QString("const B< A >"));

}

void TestDUChain::testTemplateImplicitInstantiations()
{
  {
  QByteArray method("class A { template<typename T> static void foo(T){} };\n"
                    "void test() { A::foo(5); A::foo('x'); A::foo(\"asdfasdfadf\");\n }");

  LockedTopDUContext top = parse(method, DumpNone);

  QCOMPARE(top->childContexts().first()->localDeclarations().size(), 1);
  QCOMPARE(top->localDeclarations().size(), 2);
  QCOMPARE(top->childContexts().first()->localDeclarations().size(), 1);
  TemplateDeclaration* tpl = dynamic_cast<TemplateDeclaration*>(top->childContexts().first()->localDeclarations().first());
  QVERIFY(tpl);
  QCOMPARE(tpl->instantiations().size(), 3);
  }
  {
  QByteArray method("template<typename T> void foo(T){}\n"
                    "void test() { foo(5); foo('x'); foo(\"asdfasdfadf\"); }\n");

  LockedTopDUContext top = parse(method, DumpNone);

  QCOMPARE(top->localDeclarations().size(), 2);
  TemplateDeclaration* tpl = dynamic_cast<TemplateDeclaration*>(top->localDeclarations().first());
  QVERIFY(tpl);
  QCOMPARE(tpl->instantiations().size(), 3);
  }
}

void TestDUChain::testSourceCodeInsertion()
{
  {
    QByteArray method("namespace A {\nclass B {};\n}\n");

    LockedTopDUContext top = parse(method, DumpNone);

    InsertArtificialCodeRepresentation repr(top->url(), QString::fromUtf8(method));

    QCOMPARE(top->childContexts().count(), 1);
    QCOMPARE(top->childContexts()[0]->localDeclarations().count(), 1);

    QCOMPARE(repr.text().trimmed().split('\n').count(), 3);
    {
      Cpp::SourceCodeInsertion ins(top);
      ins.insertForwardDeclaration(top->childContexts()[0]->localDeclarations()[0]);

      ins.changes().setReplacementPolicy(KDevelop::DocumentChangeSet::StopOnFailedChange);
      DocumentChangeSet::ChangeResult result = ins.changes().applyAllChanges();
      QVERIFY(result);
      kDebug() << repr.text();
//       QVERIFY(repr.text().trimmed().remove(' ').remove('\n').contains(QString("int testVar;").remove(' ')));

      //Only one newline should be added
      QCOMPARE(repr.text().trimmed().split('\n').count(), 4);
    }

    top = parse(repr.text().toUtf8(), DumpNone, top);

    QCOMPARE(top->childContexts().count(), 1);
    QCOMPARE(top->childContexts()[0]->localDeclarations().count(), 2);

  }

  {
    QByteArray method("");

    LockedTopDUContext top = parse(method, DumpNone);

    InsertArtificialCodeRepresentation repr(top->url(), QString::fromUtf8(method));

    {
      Cpp::SourceCodeInsertion ins(top);
      ins.insertFunctionDeclaration(Identifier("test"), AbstractType::Ptr(new IntegralType(IntegralType::TypeVoid)), QList<Cpp::SourceCodeInsertion::SignatureItem>(), false, "{ this is the body; }");
      ins.changes().setReplacementPolicy(KDevelop::DocumentChangeSet::StopOnFailedChange);
      DocumentChangeSet::ChangeResult result = ins.changes().applyAllChanges();
      kDebug() << result.m_failureReason;
      QVERIFY(result);
      kDebug() << repr.text();
      QVERIFY(repr.text().trimmed().remove(' ').remove('\n').contains(QString("void test() { this is the body; }").remove(' ')));

      //Only one newline should be added
      QCOMPARE(repr.text().split('\n').count(), 2);
    }

    top = parse(repr.text().toUtf8(), DumpNone, top);

    QCOMPARE(top->localDeclarations().count(), 1);

    {
      Cpp::SourceCodeInsertion ins(top);
      ins.insertVariableDeclaration(Identifier("testVar"), AbstractType::Ptr(new IntegralType(IntegralType::TypeInt)));
      ins.changes().setReplacementPolicy(KDevelop::DocumentChangeSet::StopOnFailedChange);
      DocumentChangeSet::ChangeResult result = ins.changes().applyAllChanges();
      QVERIFY(result);
      kDebug() << repr.text();
      QVERIFY(repr.text().trimmed().remove(' ').remove('\n').contains(QString("int testVar;").remove(' ')));

      //Only one newline should be added
      QCOMPARE(repr.text().split('\n').count(), 3);
    }

    top = parse(repr.text().toUtf8(), DumpNone, top);

    QCOMPARE(top->localDeclarations().count(), 2);
    QCOMPARE(top->localDeclarations()[0]->identifier(), Identifier("test"));
    QCOMPARE(top->localDeclarations()[1]->identifier(), Identifier("testVar"));

    {
      Cpp::SourceCodeInsertion ins(top);
      ins.setInsertBefore(top->localDeclarations()[1]->range().start.castToSimpleCursor());
      ins.insertForwardDeclaration(top->localDeclarations()[1]);
      ins.changes().setReplacementPolicy(KDevelop::DocumentChangeSet::StopOnFailedChange);
      DocumentChangeSet::ChangeResult result = ins.changes().applyAllChanges();
      QVERIFY(result);
      kDebug() << repr.text();
//       QVERIFY(repr.text().trimmed().remove(' ').remove('\n').contains(QString("int testVar;").remove(' ')));

      //Only one newline should be added
      QCOMPARE(repr.text().trimmed().split('\n').count(), 3);
    }

    top = parse(repr.text().toUtf8(), DumpNone, top);

    QCOMPARE(top->localDeclarations().count(), 3);
    ///@todo Wrong order (Minor issue when updating without smart-ranges)
    QCOMPARE(top->localDeclarations()[0]->identifier(), Identifier("test"));
    QCOMPARE(top->localDeclarations()[1]->identifier(), Identifier("testVar"));
    QCOMPARE(top->localDeclarations()[2]->identifier(), Identifier("testVar"));

  }
}

void TestDUChain::testSimplifiedTypeString()
{
  {
    QByteArray method("namespace A { struct B { B(); }; };");
    LockedTopDUContext top = parse(method, DumpNone);

    QCOMPARE(top->childContexts().size(), 1);
    QCOMPARE(top->childContexts()[0]->childContexts().size(), 1);
    QCOMPARE(top->childContexts()[0]->childContexts()[0]->localDeclarations().size(), 1);

    QualifiedIdentifier constructorId = top->childContexts()[0]->childContexts()[0]->localDeclarations()[0]->qualifiedIdentifier();
    QCOMPARE(constructorId.toString(), QString("A::B::B"));
    QCOMPARE(stripPrefixes(top->childContexts()[0], constructorId).toString(), QString("B::B"));
    QCOMPARE(stripPrefixes(top, constructorId).toString(), QString("A::B::B"));

  }
  {
    QByteArray method("namespace A { namespace B { struct C {}; C* foo(const C& ref, const C value); } };");
    LockedTopDUContext top = parse(method, DumpNone);

    QList<Declaration*> decls = top->findDeclarations(QualifiedIdentifier("A::B::foo"));
    QCOMPARE(decls.size(), 1);
    FunctionDeclaration* fooDecl = dynamic_cast<FunctionDeclaration*>(decls.first());
    QVERIFY(fooDecl);

    FunctionType::Ptr fooType = fooDecl->type<FunctionType>();
    QVERIFY(fooType);
    QCOMPARE(fooType->arguments().size(), 2);

    QCOMPARE(Cpp::shortenedTypeString(fooType->returnType(), top), QString("A::B::C*"));
    QCOMPARE(Cpp::shortenedTypeString(fooType->returnType(), top->childContexts()[0]), QString("B::C*"));
    QCOMPARE(Cpp::shortenedTypeString(fooType->returnType(), top->childContexts()[0]->childContexts()[0]), QString("C*"));

    // test stripping of const-ref
    AbstractType::Ptr refType = fooType->arguments().at(0);
    QVERIFY(refType.cast<ReferenceType>());
    QVERIFY(refType.cast<ReferenceType>()->baseType());
    QVERIFY(refType.cast<ReferenceType>()->baseType()->modifiers() & AbstractType::ConstModifier);

    QCOMPARE(Cpp::shortenedTypeString(refType, top), QString("const A::B::C&"));
    QCOMPARE(Cpp::shortenedTypeString(refType, top->childContexts()[0]), QString("const B::C&"));
    QCOMPARE(Cpp::shortenedTypeString(refType, top->childContexts()[0]->childContexts()[0]), QString("const C&"));

    // test stripping of const value
    AbstractType::Ptr valType = fooType->arguments().at(1);
    QVERIFY(valType->modifiers() & AbstractType::ConstModifier);
    
    QCOMPARE(Cpp::shortenedTypeString(valType, top), QString("const A::B::C"));
    QCOMPARE(Cpp::shortenedTypeString(valType, top->childContexts()[0]), QString("const B::C"));
    QCOMPARE(Cpp::shortenedTypeString(valType, top->childContexts()[0]->childContexts()[0]), QString("const C"));
  }
  {
    // a bit artificial, but similar to what you could reach with #include
    QByteArray method("namespace A { namespace B { namespace C { struct D {}; D* foo(); } } }\nnamespace A { using namespace B::C; };");
    LockedTopDUContext top = parse(method, DumpNone);

    QList<Declaration*> decls = top->findDeclarations(QualifiedIdentifier("A::B::C::foo"));
    QCOMPARE(decls.size(), 1);
    FunctionDeclaration* fooDecl = dynamic_cast<FunctionDeclaration*>(decls.first());
    QVERIFY(fooDecl);

    FunctionType::Ptr fooType = fooDecl->type<FunctionType>();
    QVERIFY(fooType);

    QCOMPARE(Cpp::shortenedTypeString(fooType->returnType(), top), QString("A::B::C::D*"));
    QCOMPARE(Cpp::shortenedTypeString(fooType->returnType(), top->childContexts()[0]), QString("B::C::D*"));
    QCOMPARE(Cpp::shortenedTypeString(fooType->returnType(), top->childContexts()[0]->childContexts()[0]), QString("C::D*"));
    QCOMPARE(Cpp::shortenedTypeString(fooType->returnType(), top->childContexts()[0]->childContexts()[0]->childContexts()[0]), QString("D*"));

    // now the interesting part: the namespace with the "using namespace B::C"
    QCOMPARE(Cpp::shortenedTypeString(fooType->returnType(), top->childContexts()[1]), QString("D*"));
  }
  {
    QByteArray method("typedef int *honk, **honk2; honk k;");
    LockedTopDUContext top = parse(method, DumpNone);

    QCOMPARE(top->localDeclarations().count(), 3);

    QVERIFY(top->localDeclarations()[0]->abstractType().cast<TypeAliasType>());
    QVERIFY(top->localDeclarations()[1]->abstractType().cast<TypeAliasType>());
    QVERIFY(top->localDeclarations()[2]->abstractType().cast<TypeAliasType>());

    QCOMPARE(top->localDeclarations()[0]->abstractType().cast<TypeAliasType>()->qualifiedIdentifier(), QualifiedIdentifier("honk"));
    QCOMPARE(top->localDeclarations()[0]->abstractType().cast<TypeAliasType>()->type()->toString(), QString("int*"));

    QCOMPARE(top->localDeclarations()[1]->abstractType().cast<TypeAliasType>()->qualifiedIdentifier(), QualifiedIdentifier("honk2"));
    ///@todo Make this work as well, the init-declarators need to have separate types
//     QCOMPARE(top->localDeclarations()[1]->abstractType().cast<TypeAliasType>()->type()->toString(), QString("int**"));

    QCOMPARE(Cpp::simplifiedTypeString(top->localDeclarations()[2]->abstractType(), top).remove(' '), QString("honk").remove(' '));
  }
  {
    QByteArray method("typedef int const * const honkolo; honkolo k;");
    LockedTopDUContext top = parse(method, DumpNone);

    QCOMPARE(top->localDeclarations().count(), 2);

    QVERIFY(top->localDeclarations()[0]->abstractType().cast<TypeAliasType>());
    QCOMPARE(Cpp::simplifiedTypeString(top->localDeclarations()[0]->abstractType().cast<TypeAliasType>()->type(), top).remove(' '), QString("const int *const").remove(' '));
  }
  {
    QByteArray method("class C; typedef C* honk; honk k;");
    LockedTopDUContext top = parse(method, DumpNone);

    QCOMPARE(top->localDeclarations().count(), 3);

    QCOMPARE(Cpp::simplifiedTypeString(top->localDeclarations()[2]->abstractType(), top).remove(' '), QString("honk").remove(' '));
  }
  {
    QByteArray method("const int i;\n");

    LockedTopDUContext top = parse(method, DumpNone);

    QCOMPARE(top->localDeclarations().count(), 1);

    QCOMPARE(Cpp::simplifiedTypeString(top->localDeclarations()[0]->abstractType(), top).remove(' '), QString("const int").remove(' '));
  }

  {
    QByteArray method("template<class T> class Template { class Member; Member mem; }; Template< Template< int >* >::Member q;\n");

    LockedTopDUContext top = parse(method, DumpNone);

    InsertArtificialCodeRepresentation repr(top->url(), QString::fromUtf8(method));

    QCOMPARE(top->localDeclarations().count(), 2);

    QCOMPARE(Cpp::simplifiedTypeString(top->localDeclarations()[1]->abstractType(), top).remove(' '), QString("Template<Template<int>*>::Member").remove(' '));
  }
  {
  ///@todo Add more tests for this
    QByteArray method("template<typename T> struct F; template<class T> class Tc; Tc<const F<int*>*> t; const Tc<const F<int*>*>**const*& Test1; Tc<F<int*>*>* test();\n");

    LockedTopDUContext top = parse(method, DumpNone);

    InsertArtificialCodeRepresentation repr(top->url(), QString::fromUtf8(method));

    QCOMPARE(top->localDeclarations().count(), 5);
    QCOMPARE(Cpp::simplifiedTypeString(top->localDeclarations()[2]->abstractType(), top).remove(' '), QString("Tc<const F<int*>*>").remove(' '));
    QCOMPARE(Cpp::simplifiedTypeString(top->localDeclarations()[3]->abstractType(), top).remove(' '), QString("constTc<const F<int*>*>**const*&").remove(' '));
    FunctionType::Ptr funType = top->localDeclarations()[4]->abstractType().cast<FunctionType>();
    QVERIFY(funType);
    QVERIFY(funType->returnType());

    //For loop is needed to test the updating, as there was a problem with that
    for(int a = 0; a < 3; ++a) {
      kDebug() << "run" << a;
      top.m_writeLock.unlock();
      parse(method, DumpNone, top);
      top.m_writeLock.lock();
      repr.setText(QString::fromUtf8(method));
      Cpp::SourceCodeInsertion ins(top);
      ins.insertFunctionDeclaration(Identifier("test"), funType->returnType(), QList<Cpp::SourceCodeInsertion::SignatureItem>(), false, "{ this is the body; }");
      ins.changes().setReplacementPolicy(KDevelop::DocumentChangeSet::StopOnFailedChange);
      DocumentChangeSet::ChangeResult result = ins.changes().applyAllChanges();
      kDebug() << result.m_failureReason;
      QVERIFY(result);
      kDebug() << repr.text();
      QVERIFY(repr.text().trimmed().remove(' ').remove('\n').contains(QString("Tc< F< int* >* >* test() { this is the body; }").remove(' ')));
      top.m_writeLock.unlock();
      parse(repr.text().toUtf8(), DumpNone, top);
      top.m_writeLock.lock();
      QVERIFY(top->localDeclarations().count() == 6);

      FunctionType::Ptr funType2 = top->localDeclarations()[5]->abstractType().cast<FunctionType>();
      QVERIFY(funType2);
      QVERIFY(funType2->returnType());
      QVERIFY(funType2->returnType()->equals(funType->returnType().unsafeData()));
      QCOMPARE(Cpp::simplifiedTypeString(funType2->returnType(), top).remove(' '), QString("Tc< F< int* >* >*").remove(' '));
    }

  }
}

void TestDUChain::testTemplateReference() {
  QByteArray method("class A; template<class T> class CC; void test(CC<const A*>& item); const A& a;const A*** b;CC<const A>  cca;");

  LockedTopDUContext top = parse(method, DumpNone);

  QCOMPARE(top->localDeclarations().count(), 6);
  QVERIFY(top->localDeclarations()[2]->abstractType());
  QCOMPARE(top->childContexts().count(), 2);
  QCOMPARE(top->childContexts()[1]->localDeclarations().count(), 1);
  AbstractType::Ptr argType = top->childContexts()[1]->localDeclarations()[0]->abstractType();
  QVERIFY(argType.cast<ReferenceType>());
  QCOMPARE(argType->toString().remove(' '), QString("CC<constA*>&"));
  {
//     QWidget* navigationWidget = top->childContexts()[1]->createNavigationWidget(top->childContexts()[1]->localDeclarations()[0]);
//     QVERIFY(navigationWidget);
//     KDevelop::AbstractNavigationWidget* nWidget = dynamic_cast<KDevelop::AbstractNavigationWidget*>(navigationWidget);
//     QVERIFY(nWidget);
//     QVERIFY(nWidget->context());
//     QString html = nWidget->context()->html();
//     kDebug() << "html:" << html;
  }
  QCOMPARE(Cpp::simplifiedTypeString(top->childContexts()[1]->localDeclarations()[0]->abstractType(), top).remove(' '), QString("CC<constA*>&"));
  QVERIFY(top->localDeclarations()[3]->abstractType());
  QCOMPARE(Cpp::simplifiedTypeString(top->localDeclarations()[3]->abstractType(), top).remove(' '), QString("constA&"));
  QCOMPARE(Cpp::simplifiedTypeString(top->localDeclarations()[4]->abstractType(), top).remove(' '), QString("constA***"));
  AbstractType::Ptr type = top->localDeclarations()[5]->abstractType();
  QVERIFY(type);
  QCOMPARE(type->toString().remove(' '), QString("CC<constA>"));
  QCOMPARE(Cpp::simplifiedTypeString(top->localDeclarations()[5]->abstractType(), top).remove(' '), QString("CC<constA>"));
}

typedef QList<QList<Declaration*> > ClassInstantiations;
struct DeclarationTestData
{
  DeclarationTestData() : useCount(0), specializedFrom(0) {};

  //Declaration
  int useCount;

  //TemplateDeclaration
  ClassInstantiations instantiations;
  QList<DeclarationTestData> instantiationsTestData;
  Declaration* specializedFrom;
  QList<Declaration*> specializations;
};
InstantiationInformation instantiationInfoForDeclarations(const QList<Declaration*> &templateParams)
{
  InstantiationInformation info;
  int paramCount = templateParams.size();
  for(int i = 0; i < paramCount; ++i)
  {
    if (templateParams[i])
      info.addTemplateParameter(templateParams[i]->abstractType());
    else
    {
      //Null param signifies the end of the template declarations for a given scope
      InstantiationInformation topInfo = instantiationInfoForDeclarations(templateParams.mid(i+1));
      topInfo.previousInstantiationInformation = info.indexed();
      return topInfo;
    }
  }
  return info;
}
bool validDeclaration(Declaration *decl, DeclarationTestData testData)
{
  qDebug() << "Validating Declaration:" << decl->toString();
  bool ret = true;
  if (decl->uses().size() != testData.useCount)
  {
    qDebug() << "Declaration use count doesn't match test data";
    qDebug() << "Actual:" << decl->uses().size() << "| Test data:" << testData.useCount;
    ret = false;
  }

  if (TemplateDeclaration *templateDecl = dynamic_cast<TemplateDeclaration*>(decl))
  {
    if (templateDecl->specializedFrom().data() != testData.specializedFrom)
    {
      qDebug() << "Declaration's specializedFrom doesn't match test data";
      qDebug() << "The declaration" << decl->toString();
      if (templateDecl->specializedFrom().declaration())
        qDebug() << "specializedFrom" <<  templateDecl->specializedFrom().declaration()->toString();
      qDebug() << "Actual:" << templateDecl->specializedFrom().data() << "| Test data:" << testData.specializedFrom;
      ret = false;
    }

    if (templateDecl->specializationsSize() != (uint)testData.specializations.size())
    {
      qDebug() << "Declaration's number of specializations doesn't match test data";
      qDebug() << "Actual:" << templateDecl->specializationsSize() << "| Test data:" << testData.specializations.size();
      ret = false;
    }
    for (uint i = 0; i < templateDecl->specializationsSize(); ++i)
    {
      if (!testData.specializations.contains(templateDecl->specializations()[i].data()))
      {
        qDebug() << "Declaration had specialization not found in test data";
        qDebug() << "Specialization not found:" << templateDecl->specializations()[i].data();
        ret = false;
      }
    }

    TemplateDeclaration::InstantiationsHash actualInstantiations = templateDecl->instantiations();
    if (actualInstantiations.size() != testData.instantiations.size())
    {
      qDebug() << "Declaration's number of instantiations doesn't match test data";
      qDebug() << "Actual:" << actualInstantiations.size() << "| Test data:" << testData.instantiations.size();
      ret = false;
    }
    for(int i = 0; i < testData.instantiations.size(); ++i)
    {
      IndexedInstantiationInformation testInfo = instantiationInfoForDeclarations(testData.instantiations[i]).indexed();
      if (!actualInstantiations.contains(testInfo))
      {
        qDebug() << "Declaration did not have an instantiation found in test data";
        qDebug() << "Instantiation not found in declaration:" << testInfo.information().toString();
        ret = false;
      }
      TemplateDeclaration *instantiation = actualInstantiations[testInfo];
      if (!instantiation)
      {
        qDebug() << "Invalid declaration for instantiation:" << testInfo.information().toString();
        ret = false;
      }
      else if (testData.instantiationsTestData.size())
      {
        if (!validDeclaration(dynamic_cast<Declaration*>(instantiation), testData.instantiationsTestData[i]))
        {
          qDebug() << "Instantiated declaration did not pass validation";
          qDebug() << "Invalid declaration:" << instantiation;
          ret = false;
        }
      }
    }
  }
  return ret;
}

void TestDUChain::testTemplatesSuper()
{
  QByteArray test = "\
class ClassA {}; //Decl 0, Ctxt 0\n\
class ClassB {};\n\
class ClassC {};\n\
class ClassD {};\n\
template<typename E_T1, typename E_T2>\n\
class ClassE\n\
{\n\
public:\n\
  template<typename E_A_T1, typename E_A_T2>\n\
  void E_FuncA(E_A_T1, E_A_T2)\n\
  {\n\
  }\n\
  void E_FuncB(E_T1, E_T2)\n\
  {\n\
  }\n\
};\n\
template<>\n\
template<>\n\
void ClassE<ClassA, ClassB>::E_FuncA<ClassA, ClassA>(ClassA, ClassA);\n\
template<>\n\
template<>\n\
void ClassE<ClassA, ClassB>::E_FuncA<ClassA, ClassA>(ClassA, ClassA)\n\
{\n\
}\n\
template<>\n\
void ClassE<ClassA, ClassB>::E_FuncB(ClassA, ClassB);\n\
template<>\n\
void ClassE<ClassA, ClassB>::E_FuncB(ClassA, ClassB)\n\
{\n\
}\n\
int main()\n\
{\n\
  ClassE<ClassA, ClassB> foo;\n\
  foo.E_FuncA(ClassA(), ClassA());\n\
  foo.E_FuncB(ClassA(), ClassB());\n\
  ClassE<ClassC, ClassD> foo2;\n\
  foo2.E_FuncA(ClassA(), ClassA());\n\
  foo2.E_FuncB(ClassC(), ClassD());\n\
}";
  LockedTopDUContext top = parse(test, DumpNone);
  int currentTopDecl = 0;
  //First gather all the visible declarations
  Declaration *ClassA = top->localDeclarations()[currentTopDecl++];
  Q_ASSERT(dynamic_cast<ClassDeclaration*>(ClassA));
  Declaration *ClassB = top->localDeclarations()[currentTopDecl++];
  Q_ASSERT(dynamic_cast<ClassDeclaration*>(ClassB));
  Declaration *ClassC = top->localDeclarations()[currentTopDecl++];
  Q_ASSERT(dynamic_cast<ClassDeclaration*>(ClassC));
  Declaration *ClassD = top->localDeclarations()[currentTopDecl++];
  Q_ASSERT(dynamic_cast<ClassDeclaration*>(ClassD));
  Declaration *ClassE = top->localDeclarations()[currentTopDecl++];
  Q_ASSERT(dynamic_cast<ClassDeclaration*>(ClassE));
  Q_ASSERT(dynamic_cast<TemplateDeclaration*>(ClassE));
    //Declarations in ClassE context
    int currentClassEDecl = 0;
    Declaration *E_FuncA = ClassE->internalContext()->localDeclarations()[currentClassEDecl++];
    Q_ASSERT(dynamic_cast<TemplateDeclaration*>(E_FuncA));
    Q_ASSERT(dynamic_cast<ClassFunctionDeclaration*>(E_FuncA));
    Declaration *E_FuncB = ClassE->internalContext()->localDeclarations()[currentClassEDecl++];
    Q_ASSERT(dynamic_cast<TemplateDeclaration*>(E_FuncB));
    Q_ASSERT(dynamic_cast<ClassFunctionDeclaration*>(E_FuncB));
  Declaration *E_A_B_FuncA_A_A_Decl = top->localDeclarations()[currentTopDecl++];
  Q_ASSERT(dynamic_cast<TemplateDeclaration*>(E_A_B_FuncA_A_A_Decl));
  Q_ASSERT(dynamic_cast<FunctionDeclaration*>(E_A_B_FuncA_A_A_Decl));
  Declaration *E_A_B_FuncA_A_A = top->localDeclarations()[currentTopDecl++];
  Q_ASSERT(dynamic_cast<TemplateDeclaration*>(E_A_B_FuncA_A_A));
  Q_ASSERT(dynamic_cast<FunctionDeclaration*>(E_A_B_FuncA_A_A));
  Q_ASSERT(dynamic_cast<FunctionDefinition*>(E_A_B_FuncA_A_A));
  Declaration *E_A_B_FuncBDecl = top->localDeclarations()[currentTopDecl++];
  Q_ASSERT(dynamic_cast<TemplateDeclaration*>(E_A_B_FuncBDecl));
  Declaration *E_A_B_FuncB = top->localDeclarations()[currentTopDecl++];
  Q_ASSERT(dynamic_cast<TemplateDeclaration*>(E_A_B_FuncB));

  Declaration *FuncMain = top->localDeclarations()[currentTopDecl++];
  QVERIFY(FuncMain);


  //Test for ClassE DUChain correctness
  DeclarationTestData ClassE_A_B_testData; ClassE_A_B_testData.useCount = 1;
  DeclarationTestData ClassE_C_D_testData; ClassE_C_D_testData.useCount = 1;
  DeclarationTestData ClassE_testData;
  ClassE_testData.useCount = 0;
  ClassE_testData.instantiations << (QList<Declaration*>() << ClassA << ClassB)
                                 << (QList<Declaration*>() << ClassC << ClassD);
  ClassE_testData.instantiationsTestData << ClassE_A_B_testData
                                         << ClassE_C_D_testData;
  QVERIFY(validDeclaration(ClassE, ClassE_testData));

  //Test for ClassE<ClassA::ClassB>::E_FuncA<ClassA::ClassA> explicit specialization forward-declaration
  //FIXME: FunctionDeclarations that aren't FunctionDefinitions are actually forward declarations, but aren't treated as such
  //According to the current (incorrect) model this decl should be the Declaration of E_A_B_FuncADef
  //However, it shouldn't be tested, as that's not technically correct anyhow
  //To be correct, E_A_B_FuncA_A_A_Decl should have a function to attempt to resolve it to E_A_B_FuncA_A_A_Def
  //and E_A_B_FuncA_A_A_Def needn't directly know of E_A_B_FuncA_A_A_Decl
  //This is because there could be multiple declarations or even no declarations for E_A_B_FuncA_A_A_Def
  DeclarationTestData E_A_B_FuncA_A_A_Decl_testData;
  QVERIFY(validDeclaration(E_A_B_FuncA_A_A_Decl, E_A_B_FuncA_A_A_Decl_testData));

  //Test for ClassE::E_FuncA
  DeclarationTestData E_A_B_FuncA_A_A_testData;
  E_A_B_FuncA_A_A_testData.useCount = 1;
  E_A_B_FuncA_A_A_testData.specializedFrom = E_FuncA;
  DeclarationTestData E_C_D_FuncA_A_A_testData;
  E_C_D_FuncA_A_A_testData.useCount = 1;
  DeclarationTestData E_C_D_FuncA_testData;
  DeclarationTestData E_A_B_FuncA_testData;
  DeclarationTestData E_FuncA_testData;
  E_FuncA_testData.specializations << E_A_B_FuncA_A_A;
  E_FuncA_testData.instantiations << (QList<Declaration*>() << ClassA << ClassB << 0 << ClassA << ClassA)
                                  << (QList<Declaration*>() << ClassC << ClassD << 0 << ClassA << ClassA)
                                  //These two instantiations shouldn't exist technically
                                  << (QList<Declaration*>() << ClassA << ClassB << 0)
                                  << (QList<Declaration*>() << ClassC << ClassD << 0);
  E_FuncA_testData.instantiationsTestData << E_A_B_FuncA_A_A_testData
                                          << E_C_D_FuncA_A_A_testData
                                          << E_A_B_FuncA_testData
                                          << E_C_D_FuncA_testData;
  QVERIFY(validDeclaration(E_FuncA, E_FuncA_testData));
  //Test for ClassE<ClassA::ClassB>::E_FuncA<ClassA::ClassA> explicit specialization definition specifically
  //even though it is automatically tested above... just because
  QVERIFY(validDeclaration(E_A_B_FuncA_A_A, E_A_B_FuncA_A_A_testData));

  DeclarationTestData E_A_B_FuncBDecl_testData;
  QVERIFY(validDeclaration(E_A_B_FuncBDecl, E_A_B_FuncBDecl_testData));

  //Test for ClassE::FuncA
  DeclarationTestData E_A_B_FuncB_testData;
  E_A_B_FuncB_testData.useCount = 1;
  E_A_B_FuncB_testData.specializedFrom = E_FuncB;
  DeclarationTestData E_C_D_FuncB_testData;
  E_C_D_FuncB_testData.useCount = 1;
  DeclarationTestData E_FuncB_testData;
  E_FuncB_testData.specializations << E_A_B_FuncB;
  E_FuncB_testData.instantiations << (QList<Declaration*>() << ClassA << ClassB << 0)
                                  << (QList<Declaration*>() << ClassC << ClassD << 0);
  E_FuncB_testData.instantiationsTestData << E_A_B_FuncB_testData
                                          << E_C_D_FuncB_testData;
  QVERIFY(validDeclaration(E_FuncB, E_FuncB_testData));
  //Test for ClassE<ClassA::ClassB>::E_FuncB explicit specialization definition specifically
  //even though it is automatically tested above... just because
  QVERIFY(validDeclaration(E_A_B_FuncB, E_A_B_FuncB_testData));
}


void TestDUChain::testTemplates() {
  QByteArray method("template<class T> T test(const T& t) {}; template<class T, class T2> class A {T2 a; typedef T Template1; }; class B{int b;}; class C{int c;}; template<class T>class A<B,T>{};  typedef A<B,C> D;");

  LockedTopDUContext top = parse(method, DumpNone);


  Declaration* defTest = top->localDeclarations()[0];
  QCOMPARE(defTest->identifier(), Identifier("test"));
  QVERIFY(defTest->type<FunctionType>());
  QVERIFY( isTemplateDeclaration(defTest) );

  Declaration* defClassA = top->localDeclarations()[1];
  QCOMPARE(defClassA->identifier(), Identifier("A"));
  QVERIFY(defClassA->type<CppClassType>());
  QVERIFY( isTemplateDeclaration(defClassA) );

  Declaration* defClassB = top->localDeclarations()[2];
  QCOMPARE(defClassB->identifier(), Identifier("B"));
  QVERIFY(defClassB->type<CppClassType>());
  QVERIFY( !isTemplateDeclaration(defClassB) );

  Declaration* defClassC = top->localDeclarations()[3];
  QCOMPARE(defClassC->identifier(), Identifier("C"));
  QVERIFY(defClassC->type<CppClassType>());
  QVERIFY( !isTemplateDeclaration(defClassC) );

  DUContext* classA = defClassA->internalContext();
  QVERIFY(classA);
  QVERIFY(classA->parentContext());
  QCOMPARE(classA->importedParentContexts().count(), 1); //The template-parameter context is imported
  QCOMPARE(classA->localScopeIdentifier(), QualifiedIdentifier("A"));

  DUContext* classB = defClassB->internalContext();
  QVERIFY(classB);
  QVERIFY(classB->parentContext());
  QCOMPARE(classB->importedParentContexts().count(), 0);
  QCOMPARE(classB->localScopeIdentifier(), QualifiedIdentifier("B"));

  DUContext* classC = defClassC->internalContext();
  QVERIFY(classC);
  QVERIFY(classC->parentContext());
  QCOMPARE(classC->importedParentContexts().count(), 0);
  QCOMPARE(classC->localScopeIdentifier(), QualifiedIdentifier("C"));

  ///Test getting the typedef for the unset template
  {
    Declaration* typedefDecl = findDeclaration(classA, Identifier("Template1"));
    QVERIFY(typedefDecl);
    QVERIFY(typedefDecl->isTypeAlias());
    QVERIFY(typedefDecl->abstractType());
    QVERIFY(unAliasedType(typedefDecl->abstractType()));
    DelayedType::Ptr delayed = unAliasedType(typedefDecl->abstractType()).cast<DelayedType>();
    QVERIFY(delayed);
    QCOMPARE(delayed->identifier(), IndexedTypeIdentifier("T"));
  }

  ///Test creating a template instance of class A<B,C>
  {
    Identifier ident("A");
    ident.appendTemplateIdentifier(IndexedTypeIdentifier("B"));
    ident.appendTemplateIdentifier(IndexedTypeIdentifier("C"));
    Declaration* instanceDefClassA = findDeclaration(top, ident);
    Declaration* instanceTypedefD = findDeclaration(top, Identifier("D"));
    QVERIFY(instanceTypedefD);
    QVERIFY(instanceDefClassA);
    QVERIFY(unAliasedType(instanceTypedefD->abstractType()));
    QCOMPARE(unAliasedType(instanceTypedefD->abstractType())->toString(), instanceDefClassA->abstractType()->toString() );
    //QCOMPARE(instanceTypedefD->abstractType().data(), instanceDefClassA->abstractType().data() ); Re-enable once specializations are re-used
    Cpp::TemplateDeclaration* templateDecl = dynamic_cast<Cpp::TemplateDeclaration*>(instanceDefClassA);
    QVERIFY(templateDecl);
    QVERIFY(instanceDefClassA != defClassA);
    QVERIFY(instanceDefClassA->context() == defClassA->context());
    QVERIFY(instanceDefClassA->internalContext() != defClassA->internalContext());
    QCOMPARE(instanceDefClassA->identifier(), Identifier("A<B,C>"));
    QCOMPARE(instanceDefClassA->identifier().toString(), QString("A< B, C >"));
    QVERIFY(instanceDefClassA->abstractType());
    AbstractType::Ptr t = instanceDefClassA->abstractType();
    IdentifiedType* identifiedType = dynamic_cast<IdentifiedType*>(t.unsafeData());
    QVERIFY(identifiedType);
    QVERIFY(identifiedType->declaration(top));
    kDebug() << identifiedType->declaration(top)->toString() << identifiedType->declaration(top)->range().castToSimpleRange().textRange() << instanceDefClassA->toString() << instanceDefClassA->range().castToSimpleRange().textRange();
    QCOMPARE(identifiedType->declaration(top), instanceDefClassA);
    QCOMPARE(identifiedType->qualifiedIdentifier().toString(), Identifier("A<B,C>").toString());
    QVERIFY(instanceDefClassA->internalContext());
    QVERIFY(instanceDefClassA->internalContext() != defClassA->internalContext());
    QVERIFY(instanceDefClassA->context() == defClassA->context());
    QVERIFY(instanceDefClassA->internalContext()->importedParentContexts().size() == 1);
    QVERIFY(defClassA->internalContext()->importedParentContexts().size() == 1);
    QCOMPARE(instanceDefClassA->internalContext()->importedParentContexts().front().context(0)->type(), DUContext::Template);
    QVERIFY(defClassA->internalContext()->importedParentContexts().front().context(0) != instanceDefClassA->internalContext()->importedParentContexts().front().context(0)); //The template-context has been instantiated

    QualifiedIdentifier ident2(ident);
    ident2.push(Identifier("Template1"));

    Declaration* template1InstanceDecl1 = findDeclaration(instanceDefClassA->internalContext(), Identifier("Template1"));
    QVERIFY(!template1InstanceDecl1);

    Declaration* template1InstanceDecl2 = findDeclaration(instanceDefClassA->internalContext(), Identifier("a"));
    QVERIFY(!template1InstanceDecl2);

    Declaration* template1InstanceDecl = findDeclaration(top, ident2);
    QVERIFY(!template1InstanceDecl);
  }

}

void TestDUChain::testTemplateParameters() {
  return;
  QByteArray method("template<class T, class N = T, int q = 5> class A {};");

  LockedTopDUContext top = parse(method, DumpNone);

  QCOMPARE(top->childContexts().count(), 2);
  QCOMPARE(top->childContexts()[0]->type(), DUContext::Template);
  QCOMPARE(top->childContexts()[0]->localDeclarations().count(), 3);

  Declaration* param1 = top->childContexts()[0]->localDeclarations()[0];
  QVERIFY(param1->type<CppTemplateParameterType>());

  Declaration* param2 = top->childContexts()[0]->localDeclarations()[1];
  QVERIFY(param2->type<CppTemplateParameterType>());
  TemplateParameterDeclaration* param2Decl = dynamic_cast<TemplateParameterDeclaration*>(param2);
  QVERIFY(param2Decl);
  QVERIFY(!param2Decl->defaultParameter().isEmpty());

  Declaration* param3 = top->childContexts()[0]->localDeclarations()[2];
  QVERIFY(param3->type<IntegralType>());
  TemplateParameterDeclaration* param3Decl = dynamic_cast<TemplateParameterDeclaration*>(param3);
  QVERIFY(param3Decl);
  QVERIFY(!param3Decl->defaultParameter().isEmpty());

}

void TestDUChain::testTemplateDefaultParameters() {
  QByteArray method("struct S {} ; namespace std { template<class T> class Template1 { }; } template<class _TT, typename TT2 = std::Template1<_TT> > class Template2 { typedef TT2 T1; };");

  LockedTopDUContext top = parse(method, DumpNone);

  kDebug() << "searching";
//  Declaration* memberDecl = findDeclaration(top, QualifiedIdentifier("Template2<S>::T1"));
  Declaration* memberDecl = findDeclaration(top, QualifiedIdentifier("Template2<S>::TT2"));
  QVERIFY(memberDecl);
  QVERIFY(memberDecl->abstractType());
  QVERIFY(!memberDecl->type<DelayedType>());
  QCOMPARE(memberDecl->abstractType()->toString(), QString("std::Template1< S >"));

}

void TestDUChain::testTemplates3() {
  QByteArray method("typedef int quakka; template<class T> struct Test { typedef T Value; const Value cv; const T cv2; ; typedef Value& ValueRef; typedef const ValueRef ConstValueRef; };");

  LockedTopDUContext top = parse(method, DumpNone);

  Declaration* qDecl = findDeclaration(top, QualifiedIdentifier("quakka"));
  QVERIFY(qDecl);
  QCOMPARE(qDecl->abstractType()->toString(), QString("quakka"));

  Declaration* cvDecl = findDeclaration(top, QualifiedIdentifier("Test<quakka>::cv"));
  QVERIFY(cvDecl);
  QVERIFY(cvDecl->abstractType());

  AbstractType::Ptr type = cvDecl->abstractType();
  IdentifiedType* idType = dynamic_cast<IdentifiedType*>(type.unsafeData());
  QVERIFY(idType);
  QVERIFY(idType->declaration(top));

  QVERIFY(cvDecl->abstractType()->modifiers() & AbstractType::ConstModifier);
  QVERIFY(TypeUtils::unAliasedType(cvDecl->abstractType())->modifiers() & AbstractType::ConstModifier);

  QCOMPARE(unAliasedType(cvDecl->abstractType())->toString(), QString("const int"));
  Declaration* cv2Decl = findDeclaration(top, QualifiedIdentifier("Test<quakka>::cv2"));
  QVERIFY(cv2Decl);
  QVERIFY(cv2Decl->abstractType());
  QVERIFY(cv2Decl->abstractType()->modifiers() & AbstractType::ConstModifier);
  QCOMPARE(cv2Decl->abstractType()->toString(), QString("const quakka"));
  QCOMPARE(unAliasedType(cv2Decl->abstractType())->toString(), QString("const int"));
  QVERIFY(TypeUtils::unAliasedType(cv2Decl->abstractType())->modifiers() & AbstractType::ConstModifier);

  {
    Declaration* cvrDecl = findDeclaration(top, QualifiedIdentifier("Test<quakka>::ConstValueRef"));
    QVERIFY(cvrDecl);

    AbstractType::Ptr type = cvrDecl->abstractType();
    IdentifiedType* idType = dynamic_cast<IdentifiedType*>(type.unsafeData());
    QVERIFY(idType);
    QVERIFY(idType->declaration(top));

    QVERIFY(cvrDecl->abstractType());
    QVERIFY(unAliasedType(cvrDecl->abstractType())->modifiers() & AbstractType::ConstModifier);
    QCOMPARE(realType(cvrDecl->abstractType(), 0)->toString(), QString("const int"));
    QCOMPARE(targetType(cvrDecl->abstractType(), 0)->toString(), QString("const int"));
    TypeAliasType::Ptr alias = cvrDecl->abstractType().cast<TypeAliasType>();
    QVERIFY(alias);
    QVERIFY(alias->type());
    QCOMPARE(targetTypeKeepAliases(Cpp::shortenTypeForViewing(cvrDecl->abstractType()), 0, 0)->toString(), QString("const quakka"));
    //When the target is a typedef type, the duchain does not know whether it should write "const" before or behind, so it puts it behind "quakka"
    QCOMPARE(Cpp::shortenTypeForViewing(cvrDecl->abstractType())->toString(), QString("quakka const&"));
    QVERIFY(TypeUtils::unAliasedType(cvrDecl->abstractType())->modifiers() & AbstractType::ConstModifier);
  }
  {
    Declaration* cvrDecl = findDeclaration(top, QualifiedIdentifier("Test<quakka*>::ConstValueRef"));
    QVERIFY(cvrDecl);
    QVERIFY(cvrDecl->abstractType());
    QVERIFY(unAliasedType(cvrDecl->abstractType())->modifiers() & AbstractType::ConstModifier);
    QCOMPARE(realType(cvrDecl->abstractType(), 0)->toString(), QString("quakka* const"));
    TypeAliasType::Ptr alias = cvrDecl->abstractType().cast<TypeAliasType>();
    QVERIFY(alias);
    QVERIFY(alias->type());
    QCOMPARE(Cpp::shortenTypeForViewing(cvrDecl->abstractType())->toString(), QString("quakka* const&"));
    QVERIFY(TypeUtils::unAliasedType(cvrDecl->abstractType())->modifiers() & AbstractType::ConstModifier);

  }
  {
    QByteArray method("template<class T> struct Cnt { typedef T Val; }; struct Item; template<class Value> struct Freqto { struct Item { typedef Value Value2; }; struct Pattern : public Cnt<Item> { }; };");

    LockedTopDUContext top = parse(method, DumpNone);
    QCOMPARE(top->childContexts().count(), 4);
    QCOMPARE(top->childContexts()[3]->childContexts().count(), 2);

    //The import should have been delayed, since it needs 'Item'
    QVERIFY(top->childContexts()[3]->childContexts()[1]->owner());
    ClassDeclaration* classDecl = dynamic_cast<ClassDeclaration*>(top->childContexts()[3]->childContexts()[1]->owner());
    QVERIFY(classDecl);
    QCOMPARE(classDecl->baseClassesSize(), 1u);
    QVERIFY(top->childContexts()[3]->childContexts()[1]->importedParentContexts().isEmpty());
    QVERIFY(classDecl->baseClasses()[0].baseClass.type<DelayedType>());
    kDebug() << classDecl->baseClasses()[0].baseClass.abstractType()->toString();
    Declaration* val2Decl = findDeclaration(top, QualifiedIdentifier("Freqto<int>::Pattern::Val::Value2"));
    QVERIFY(val2Decl);
    QCOMPARE(unAliasedType(val2Decl->abstractType())->toString(), QString("int"));

  }
}

void TestDUChain::testTemplates4()
{
  {
    QByteArray method("template<class A> class Temp { typedef A Mem; }; class B {class A { typedef int AMember; }; }; ");

    LockedTopDUContext top = parse(method, DumpNone);

    QCOMPARE(top->childContexts().count(), 3);
    Declaration* memDecl = findDeclaration(top->childContexts()[2], QualifiedIdentifier("Temp<A>::Mem"));;
    QVERIFY(memDecl);
    QCOMPARE(unAliasedType(memDecl->abstractType())->toString(), QString("B::A"));
    QVERIFY(findDeclaration(top->childContexts()[2], QualifiedIdentifier("Temp<A>::Mem::AMember")));

  }
  {
    QByteArray method("template<class T> struct Cnt { typedef T Val; }; struct Item; template<class Value> struct Freqto { struct Item { typedef Value Value2; }; struct Pattern : public Cnt<Item> { }; };");

    LockedTopDUContext top = parse(method, DumpNone);
    QCOMPARE(top->childContexts().count(), 4);
    QCOMPARE(top->childContexts()[3]->childContexts().count(), 2);

    //The import should have been delayed, since it needs 'Item'
    QVERIFY(top->childContexts()[3]->childContexts()[1]->owner());
    ClassDeclaration* classDecl = dynamic_cast<ClassDeclaration*>(top->childContexts()[3]->childContexts()[1]->owner());
    QVERIFY(classDecl);
    QCOMPARE(classDecl->baseClassesSize(), 1u);
    QVERIFY(top->childContexts()[3]->childContexts()[1]->importedParentContexts().isEmpty());
    QVERIFY(classDecl->baseClasses()[0].baseClass.type<DelayedType>());
    kDebug() << classDecl->baseClasses()[0].baseClass.abstractType()->toString();
    Declaration* val2Decl = findDeclaration(top, QualifiedIdentifier("Freqto<int>::Pattern::Val::Value2"));
    QVERIFY(val2Decl);
    QCOMPARE(unAliasedType(val2Decl->abstractType())->toString(), QString("int"));

  }
}

void TestDUChain::testTemplates2() {
  QByteArray method("struct S {} ; template<class TT> class Base { struct Alloc { typedef TT& referenceType; }; }; template<class T> struct Class : public Base<T> { typedef typename Base<T>::Alloc Alloc; typedef typename Alloc::referenceType reference; reference member; }; Class<S*&> instance;");

  LockedTopDUContext top = parse(method, DumpNone);

  QVERIFY(top->childContexts().count() > 2);
  QCOMPARE(top->childContexts()[1]->localDeclarations().count(), 1);
  QVERIFY(!top->childContexts()[1]->localDeclarations()[0]->type<TypeAliasType>());
  QVERIFY(top->childContexts().count() == 5);
  QVERIFY(top->childContexts()[4]->localDeclarations().count() > 1);

  QVERIFY(top->childContexts()[4]->localDeclarations()[0]->type<TypeAliasType>());
  QVERIFY(top->childContexts()[4]->localDeclarations()[1]->type<TypeAliasType>());
  QVERIFY(top->childContexts()[4]->localDeclarations()[0]->type<TypeAliasType>()->type().cast<DelayedType>());
  QVERIFY(top->childContexts()[4]->localDeclarations()[1]->type<TypeAliasType>()->type().cast<DelayedType>());
  QVERIFY(top->childContexts()[4]->localDeclarations()[2]->type<DelayedType>());

  Declaration* memberDecl;

  kDebug() << "checking member";

  memberDecl = findDeclaration(top, QualifiedIdentifier("Class<S>::member"));
  QVERIFY(memberDecl);
  QVERIFY(unAliasedType(memberDecl->abstractType()));
  QCOMPARE(unAliasedType(memberDecl->abstractType())->toString(), QString("S&"));

  memberDecl = findDeclaration(top, QualifiedIdentifier("instance"));
  QVERIFY(memberDecl);
  QVERIFY(memberDecl->abstractType());
  QCOMPARE(memberDecl->abstractType()->toString(), QString("Class< S*& >"));

//   memberDecl = findDeclaration(top, QualifiedIdentifier("Class<S>::Alloc<S>::referenceType"));
//   QVERIFY(memberDecl);
//   QVERIFY(memberDecl->abstractType());
//   QCOMPARE(memberDecl->abstractType()->toString(), QString("S&"/*));

  memberDecl = findDeclaration(top, QualifiedIdentifier("Class<S*>::member"));
  QVERIFY(memberDecl);
  QVERIFY(unAliasedType(memberDecl->abstractType()));
  QCOMPARE(unAliasedType(memberDecl->abstractType())->toString(), QString("S*&"));

}

void TestDUChain::testSpecializationSelection()
{
  QByteArray method("template<class T1> struct Foo { void normal() {} };\n"
                    "template<class T1> struct Foo<const T1> { void typeIsConst() {} };\n"
                    "template<class T1> struct Foo<volatile T1> { void typeIsVolatile() {} };\n"
                    "template<class T1> struct Foo<const volatile T1> { void typeIsConstVolatile() {} };\n"
                    "template<class T1> struct Foo<T1*> { void typeIsPtr() {} };\n"
                    "template<class T1> struct Foo<T1**> { void typeIsPtrPtr() {} };\n"
                    "template<class T1> struct Foo<const T1*> { void typeIsConstPtr() {} };\n"
                    "template<class T1> struct Foo<const T1**> { void typeIsConstPtrPtr() {} };\n"
                    "template<class T1> struct Foo<T1&&> { void typeIsRValue() {} };\n"
                    "template<class T1> struct Foo<const T1&&> { void typeIsConstRValue() {} };\n"
                    "template<class T1> struct Foo<T1[]> { void typeIsArray() {} };\n"
                    "template<class T1> struct Foo<const T1[]> { void typeIsConstArray() {} };\n"
                    "template<class T1> struct Foo<const T1*&&> { void typeIsConstPtrRValue() {} };\n"
                    "template<class T1> struct Foo<const T1* const &&> { void typeIsConstPtrConstRValue() {} };\n");
  LockedTopDUContext top = parse(method, DumpNone);
  QVERIFY(findDeclaration(top, QualifiedIdentifier("Foo<int>::normal")));
  QVERIFY(findDeclaration(top, QualifiedIdentifier("Foo<const int>::typeIsConst")));
  QVERIFY(findDeclaration(top, QualifiedIdentifier("Foo<volatile int>::typeIsVolatile")));
  QVERIFY(findDeclaration(top, QualifiedIdentifier("Foo<const volatile int>::typeIsConstVolatile")));
  QVERIFY(findDeclaration(top, QualifiedIdentifier("Foo<int*>::typeIsPtr")));
  QVERIFY(findDeclaration(top, QualifiedIdentifier("Foo<const int*>::typeIsConstPtr")));
  QVERIFY(findDeclaration(top, QualifiedIdentifier("Foo<int&&>::typeIsRValue")));
  QVERIFY(findDeclaration(top, QualifiedIdentifier("Foo<const int&&>::typeIsConstRValue")));
  QVERIFY(findDeclaration(top, QualifiedIdentifier("Foo<const int***>::typeIsPtrPtr")));
  QVERIFY(findDeclaration(top, QualifiedIdentifier("Foo<const int**>::typeIsConstPtrPtr")));
  QVERIFY(findDeclaration(top, QualifiedIdentifier("Foo<int[]>::typeIsArray")));
  QVERIFY(findDeclaration(top, QualifiedIdentifier("Foo<const int[]>::typeIsConstArray")));
  QVERIFY(findDeclaration(top, QualifiedIdentifier("Foo<const int*&&>::typeIsConstPtrRValue")));
  QVERIFY(findDeclaration(top, QualifiedIdentifier("Foo<const int*const&&>::typeIsConstPtrConstRValue")));
  QVERIFY(findDeclaration(top, QualifiedIdentifier("Foo<Foo<int>>::normal")));
}

void TestDUChain::testSpecializationSelection2()
{
  QByteArray method("template<class T1> struct Simple {};\n"
                    "template<class T1> struct Simple<T1&> {};\n"
                    "template<class T1, class T2> struct Foo { void normal() {} };\n"
                    "template<class T1> struct Foo<int, T1> { void normal2() {} };\n"
                    "template<class T1> struct Foo<T1&, int> { void normal3() {} };\n"
                    "template<class T1> struct Foo<Simple<T1>, int> { void normal4() {} };\n"
                    "template<class T1> struct Foo<int, Simple<T1&>> { void normal5() {} };\n");
  LockedTopDUContext top = parse(method, DumpNone);
  QVERIFY(findDeclaration(top, QualifiedIdentifier("Foo<char, int>::normal")));
  QVERIFY(findDeclaration(top, QualifiedIdentifier("Foo<int, int>::normal2")));
  QVERIFY(findDeclaration(top, QualifiedIdentifier("Foo<int&, int>::normal3")));
  QVERIFY(findDeclaration(top, QualifiedIdentifier("Foo<Simple<int>, int>::normal4")));
  QVERIFY(findDeclaration(top, QualifiedIdentifier("Foo<int, Simple<int&>>::normal5")));
}

void TestDUChain::testTemplatesRebind() {
  QByteArray method("struct A {}; struct S {typedef A Value;} ; template<class TT> class Base { template<class T> struct rebind { typedef Base<T> other; }; typedef TT Type; }; template<class T> class Class { typedef Base<T>::rebind<T>::other::Type MemberType; MemberType member; Base<T>::template rebind<T>::other::Type member2; T::Value value; };");

  LockedTopDUContext top = parse(method, DumpNone);

  QCOMPARE(top->childContexts().count(), 6);
  QCOMPARE(top->childContexts()[3]->childContexts().count(), 2);
  QCOMPARE(top->childContexts()[3]->childContexts()[1]->localDeclarations().count(), 1);
  {
  QVERIFY(findDeclaration(top, QualifiedIdentifier("Base<S>")));
  QVERIFY(!findDeclaration(top, QualifiedIdentifier("Base<S>"))->type<DelayedType>());
  QVERIFY(findDeclaration(top, QualifiedIdentifier("Base<S>::rebind<A>")));
  QVERIFY(!findDeclaration(top, QualifiedIdentifier("Base<S>::rebind<A>::other"))->type<DelayedType>());
  QVERIFY(findDeclaration(top, QualifiedIdentifier("Base<S>::rebind<A>::other::Type")));
  QVERIFY(!findDeclaration(top, QualifiedIdentifier("Base<S>::rebind<A>::other::Type"))->type<DelayedType>());

  Declaration* memberDecl = findDeclaration(top, QualifiedIdentifier("Base<S>::rebind<A>::other::Type"));
  QVERIFY(memberDecl);
  QVERIFY(memberDecl->abstractType());
  QVERIFY(memberDecl->abstractType());
  QVERIFY(unAliasedType(memberDecl->abstractType()));
  QCOMPARE(unAliasedType(memberDecl->abstractType())->toString(), QString("A"));
  }

  Declaration* memberDecl = findDeclaration(top, QualifiedIdentifier("Class<S>::member"));
  QVERIFY(memberDecl);
  QVERIFY(memberDecl->abstractType());
  QVERIFY(unAliasedType(memberDecl->abstractType()));
  QCOMPARE(unAliasedType(memberDecl->abstractType())->toString(), QString("S"));

  Declaration* member3Decl = findDeclaration(top, QualifiedIdentifier("Class<S>::value"));
  QVERIFY(member3Decl);
  QVERIFY(member3Decl->abstractType());
  QVERIFY(unAliasedType(member3Decl->abstractType()));
  QCOMPARE(unAliasedType(member3Decl->abstractType())->toString(), QString("A"));

  Declaration* member2Decl = findDeclaration(top, QualifiedIdentifier("Class<S>::member2"));
  QVERIFY(member2Decl);
  QVERIFY(unAliasedType(member2Decl->abstractType()));
  QCOMPARE(unAliasedType(member2Decl->abstractType())->toString(), QString("S"));

}

void TestDUChain::testTemplatesRebind2() {
  QByteArray method("struct A {}; struct S {typedef A Value;} ;template<class T> class Test { Test(); }; template<class T> class Class { typedef typename T::Value Value; typename T::Value value; typedef Test<Value> ValueClass; Test<const Value> ValueClass2;};");

  LockedTopDUContext top = parse(method, DumpNone);

  QList<Declaration*> constructors = TypeUtils::getConstructors( top->localDeclarations()[2]->abstractType().cast<CppClassType>(), top );
  QCOMPARE(constructors.size(), 1);
  OverloadResolver resolution( DUContextPointer(top->localDeclarations()[2]->internalContext()), TopDUContextPointer(top) );
  QVERIFY(resolution.resolveConstructor( OverloadResolver::ParameterList() ));

  QVERIFY(top->childContexts().count() >= 6);
  QCOMPARE(top->childContexts()[5]->localDeclarations().count(), 4);
  AbstractType::Ptr valueType = top->childContexts()[5]->localDeclarations()[0]->abstractType();
  QVERIFY(containsDelayedType(valueType));
  QVERIFY(containsDelayedType(top->childContexts()[5]->localDeclarations()[1]->abstractType()));
  QVERIFY(containsDelayedType(top->childContexts()[5]->localDeclarations()[2]->abstractType()));
  QVERIFY(containsDelayedType(top->childContexts()[5]->localDeclarations()[3]->abstractType()));

  Declaration* valueAliasDecl = findDeclaration(top, QualifiedIdentifier("Class<S>::Value"));
  QVERIFY(valueAliasDecl);
  TypeAliasType::Ptr alias = valueAliasDecl->type<TypeAliasType>();
  QVERIFY(alias);
  QVERIFY(alias->type());
  QVERIFY(!alias->type().cast<DelayedType>());
  QCOMPARE(unAliasedType(alias.cast<AbstractType>())->toString(), QString("A"));
  kDebug() << "aliased type:" << alias->type()->toString();
  kDebug() << "typedef type:" << alias->toString();
  kDebug() << "un-aliased type:" << unAliasedType(alias.cast<AbstractType>())->toString();

  Declaration* member5Decl = findDeclaration(top, QualifiedIdentifier("Class<S>::ValueClass2"));
  QVERIFY(member5Decl);
  AbstractType::Ptr type = unAliasedType(member5Decl->abstractType());
  QVERIFY(type);
  QCOMPARE(type->toString(), QString("Test< S::Value >")); ///@todo This will fail once we parse "const" correctly, change it to "Test< const A >" then

  Declaration* member4Decl = findDeclaration(top, QualifiedIdentifier("Class<S>::ValueClass"));
  QVERIFY(member4Decl);
  QVERIFY(unAliasedType(member4Decl->abstractType()));
  QCOMPARE(unAliasedType(member4Decl->abstractType())->toString(), QString("Test< S::Value >"));

  Declaration* member3Decl = findDeclaration(top, QualifiedIdentifier("Class<S>::value"));
  QVERIFY(member3Decl);
  QVERIFY(unAliasedType(member3Decl->abstractType()));
  QCOMPARE(unAliasedType(member3Decl->abstractType())->toString(), QString("A"));

}

void TestDUChain::testForwardDeclaration()
{
  QByteArray method("class Test; Test t; class Test {int i; class SubTest; }; Test::SubTest t2; class Test::SubTest{ int i;};");

  LockedTopDUContext top = parse(method, DumpNone);

  QVERIFY(top->inSymbolTable());
  QCOMPARE(top->localDeclarations().count(), 4); //Test::SubTest is in a prefix context
  QCOMPARE(top->childContexts().count(), 2); //Test::SubTest is in a prefix context
  QCOMPARE(top->childContexts()[1]->localDeclarations().count(), 1); //Test::SubTest is in a prefix context

  QVERIFY(dynamic_cast<ForwardDeclaration*>(top->localDeclarations()[0]));
  QVERIFY(top->localDeclarations()[0]->inSymbolTable());
  QVERIFY(top->localDeclarations()[1]->inSymbolTable());
  QVERIFY(top->localDeclarations()[2]->inSymbolTable());
  QVERIFY(top->localDeclarations()[3]->inSymbolTable());
  QVERIFY(!top->localDeclarations()[2]->isForwardDeclaration());

  QCOMPARE(top->localDeclarations()[0]->additionalIdentity(), top->localDeclarations()[2]->additionalIdentity());
  QVERIFY(!dynamic_cast<TemplateDeclaration*>(top->localDeclarations()[0]));
  QVERIFY(!dynamic_cast<TemplateDeclaration*>(top->localDeclarations()[2]));

  CppClassType::Ptr type1 = top->localDeclarations()[0]->type<CppClassType>();
  kDebug() << typeid(*top->localDeclarations()[1]->abstractType()).name();
  CppClassType::Ptr type2 = top->localDeclarations()[1]->type<CppClassType>();
  CppClassType::Ptr type3 = top->localDeclarations()[2]->type<CppClassType>();
  CppClassType::Ptr type4 = top->localDeclarations()[3]->type<CppClassType>();
  CppClassType::Ptr type5 = top->childContexts()[1]->localDeclarations()[0]->type<CppClassType>();


  QCOMPARE(top->localDeclarations()[0]->kind(), Declaration::Type);
  QCOMPARE(top->localDeclarations()[1]->kind(), Declaration::Instance);
  QCOMPARE(top->localDeclarations()[2]->kind(), Declaration::Type);
  QCOMPARE(top->localDeclarations()[3]->kind(), Declaration::Instance);
  QCOMPARE(top->childContexts()[1]->localDeclarations()[0]->kind(), Declaration::Type);
  QVERIFY(type1);
  QVERIFY(type2);
  QVERIFY(type3);
  QVERIFY(type4);
  QVERIFY(type5);

  Declaration* TestDecl = top->localDeclarations()[2];
  QVERIFY(TestDecl->internalContext());
  QCOMPARE(TestDecl->internalContext()->localDeclarations().count(), 2);

  CppClassType::Ptr subType = TestDecl->internalContext()->localDeclarations()[1]->type<CppClassType>();
  QVERIFY(subType);

  QCOMPARE(subType->declaration(0)->abstractType()->indexed(), type5->indexed());
  QCOMPARE(type1->indexed(), type2->indexed());
  QCOMPARE(type1->declaration(top), type2->declaration(top));
  QVERIFY(type1->equals(type3.unsafeData()));
  QVERIFY(type3->equals(type1.unsafeData()));
  QCOMPARE(type1->declaration(0)->abstractType()->indexed(), type3->indexed());
  kDebug() << typeid(*type3->declaration(top)).name();
  kDebug() << typeid(*type1->declaration(top)).name();
  QCOMPARE(type1->declaration(top)->logicalInternalContext(0), type3->declaration(top)->internalContext());
  QCOMPARE(type2->declaration(top)->logicalInternalContext(0), type3->declaration(top)->internalContext());

  kDebug() << subType->qualifiedIdentifier().toString(); //declaration(0)->toString();
  kDebug() << type5->qualifiedIdentifier().toString(); //declaration(0)->toString();

}

void TestDUChain::testCaseUse()
{
  QByteArray method("enum Bla { Val }; char* c; int a; void test() { switch(a) { case Val: a += 1; break; } delete c; }   ");

  LockedTopDUContext top = parse(method, DumpNone);

  QCOMPARE(top->localDeclarations().count(), 4);
  QVERIFY(top->localDeclarations()[0]->internalContext());
  QCOMPARE(top->localDeclarations()[0]->internalContext()->localDeclarations().count(), 1);
  QCOMPARE(top->localDeclarations()[0]->internalContext()->localDeclarations()[0]->uses().count(), 1);
  QCOMPARE(top->localDeclarations()[1]->uses().count(), 1);
  QCOMPARE(top->localDeclarations()[0]->internalContext()->localDeclarations()[0]->uses().begin()->count(), 1);
  QCOMPARE(top->localDeclarations()[1]->uses().begin()->count(), 1);

}

void TestDUChain::testSizeofUse()
{
  QByteArray method("class C{}; const unsigned int i = sizeof(C);");

  LockedTopDUContext top = parse(method, DumpNone);

  QCOMPARE(top->localDeclarations().count(), 2);
  QCOMPARE(top->localDeclarations()[0]->uses().count(), 1);

}

void TestDUChain::testDefinitionUse()
{
  QByteArray method("class A{}; class C : public A{ void test(); }; C::test() {} ");

  LockedTopDUContext top = parse(method, DumpNone);

  QCOMPARE(top->localDeclarations().count(), 3);
  QCOMPARE(top->localDeclarations()[0]->uses().count(), 1);
  QCOMPARE(top->localDeclarations()[1]->uses().count(), 1);
}

void TestDUChain::testOperatorUses()
{
  {
    QByteArray method("template<class T> struct Fruk { Fruk<T>& operator[](int); }; Fruk<int> f; void test(){ const int mog; Fruk q (f[mog]); }");

    LockedTopDUContext top = parse(method, DumpAll);

    QCOMPARE(top->childContexts().count(), 4);
    QCOMPARE(top->childContexts()[3]->localDeclarations().size(), 2);
    QCOMPARE(top->childContexts()[3]->localDeclarations()[0]->uses().size(), 1);

  }
  {
    QByteArray method("struct S { bool operator() () const {}  };void test() { S s; s(); S()(); } ");

    LockedTopDUContext top = parse(method, DumpNone);

    QCOMPARE(top->localDeclarations().count(), 2);
    QCOMPARE(top->localDeclarations()[0]->uses().size(), 1);
    QCOMPARE(top->localDeclarations()[0]->uses().begin()->size(), 2);
    QCOMPARE(top->childContexts().count(), 3);
    QCOMPARE(top->childContexts()[0]->localDeclarations().count(), 1);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[0]->uses().size(), 1);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[0]->uses().begin()->size(), 2);

  }
  {
    QByteArray method("struct S { S operator() () const {}; S(int) {};  };void test() { S s(1); s(); S(1)()(); } ");

    LockedTopDUContext top = parse(method, DumpNone);

    QCOMPARE(top->localDeclarations().count(), 2);
    QCOMPARE(top->localDeclarations()[0]->uses().size(), 1);
    QCOMPARE(top->localDeclarations()[0]->uses().begin()->size(), 3);
    QCOMPARE(top->childContexts().count(), 3);
    QCOMPARE(top->childContexts()[0]->localDeclarations().count(), 2);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[0]->uses().size(), 1);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[0]->uses().begin()->size(), 3);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().size(), 1);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().begin()->size(), 2);
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().begin()->at(0).castToSimpleRange().textRange(), KTextEditor::Range(0, 68, 0, 69));
    QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().begin()->at(1).castToSimpleRange().textRange(), KTextEditor::Range(0, 79, 0, 80));

  }
  {
    //                 0         1         2         3         4         5
    //                 012345678901234567890123456789012345678901234567890123456789
    QByteArray method("struct foo { bool operator==(const foo&){} };\n"
                      "int main() { foo t1; foo t2;\n"
                      // not valid in global context, hence put it into main
                      "bool b1 = t1 == t2;\n"
                      "bool b2 = t1.operator==(t2); }");

    LockedTopDUContext top = parse(method, DumpAll);
    QCOMPARE(top->localDeclarations().count(), 2);
    QCOMPARE(top->childContexts().first()->localDeclarations().size(), 1);
    QCOMPARE(top->childContexts().first()->localDeclarations().first()->uses().count(), 1);
    QCOMPARE(top->childContexts().first()->localDeclarations().first()->uses().begin()->size(), 2);
    QCOMPARE(top->childContexts().first()->localDeclarations().first()->uses().begin()->at(0), RangeInRevision(2, 13, 2, 15));
    QCOMPARE(top->childContexts().first()->localDeclarations().first()->uses().begin()->at(1), RangeInRevision(3, 13, 3, 23));

  }
  {
    //                 0         1         2         3         4         5
    //                 012345678901234567890123456789012345678901234567890123456789
    QByteArray method("struct foo { foo& operator=(const foo&){} };\n"
                      "int main() { foo t1; foo t2 = t1;\n"
                      // not valid in global context, hence put it into main
                      "t1.operator=(t2); return 0; }");

    LockedTopDUContext top = parse(method, DumpNone);
    QCOMPARE(top->localDeclarations().count(), 2);
    QCOMPARE(top->childContexts().first()->localDeclarations().size(), 1);
    QCOMPARE(top->childContexts().first()->localDeclarations().first()->uses().count(), 1);
    QCOMPARE(top->childContexts().first()->localDeclarations().first()->uses().begin()->size(), 2);
    QCOMPARE(top->childContexts().first()->localDeclarations().first()->uses().begin()->at(0), RangeInRevision(1, 28, 1, 29));
    QCOMPARE(top->childContexts().first()->localDeclarations().first()->uses().begin()->at(1), RangeInRevision(2, 3, 2, 12));

  }
}

void TestDUChain::testForwardDeclaration2()
{
  QByteArray method("class Test; Test t; class Test {int i; class SubTest; }; class Test; Test::SubTest t2; class Test::SubTest{ int i;};");

  LockedTopDUContext top = parse(method, DumpNone);

  QCOMPARE(top->localDeclarations().count(), 5);
  QCOMPARE(top->childContexts().count(), 2);
  QCOMPARE(top->childContexts()[1]->localDeclarations().count(), 1);

  QCOMPARE(top->childContexts()[1]->scopeIdentifier(), QualifiedIdentifier("Test")); //This is the prefix-context around Test::SubTest

  QVERIFY(dynamic_cast<ForwardDeclaration*>(top->localDeclarations()[0]));

  CppClassType::Ptr type1 = top->localDeclarations()[0]->type<CppClassType>();
  CppClassType::Ptr type2 = top->localDeclarations()[1]->type<CppClassType>();
  CppClassType::Ptr type3 = top->localDeclarations()[2]->type<CppClassType>();
  CppClassType::Ptr type4 = top->localDeclarations()[4]->type<CppClassType>();
  CppClassType::Ptr type5 = top->childContexts()[1]->localDeclarations()[0]->type<CppClassType>();
  CppClassType::Ptr type12 = top->localDeclarations()[3]->type<CppClassType>();

  QCOMPARE(top->localDeclarations()[0]->kind(), Declaration::Type);
  QCOMPARE(top->localDeclarations()[1]->kind(), Declaration::Instance);
  QCOMPARE(top->localDeclarations()[2]->kind(), Declaration::Type);
  QCOMPARE(top->localDeclarations()[3]->kind(), Declaration::Type);
  QCOMPARE(top->localDeclarations()[4]->kind(), Declaration::Instance);
  QCOMPARE(top->childContexts()[1]->localDeclarations()[0]->kind(), Declaration::Type);

  QVERIFY(type1);
  QVERIFY(type12);
  QVERIFY(type2);
  QVERIFY(type3);
  QVERIFY(type4);
  QVERIFY(type5);

  Declaration* TestDecl = top->localDeclarations()[2];
  QVERIFY(TestDecl->internalContext());
  QCOMPARE(TestDecl->internalContext()->localDeclarations().count(), 2);

  CppClassType::Ptr subType = TestDecl->internalContext()->localDeclarations()[1]->type<CppClassType>();
  QVERIFY(subType);

  QCOMPARE(type1->declaration(0)->abstractType()->indexed(), type2->declaration(0)->abstractType()->indexed());
  QCOMPARE(type2->declaration(0)->abstractType()->indexed(), type3->indexed());
  QCOMPARE(type1->declaration(0)->abstractType()->indexed(), type12->declaration(0)->abstractType()->indexed());
  kDebug() << type4->qualifiedIdentifier().toString();
  kDebug() << type4->declaration(0)->toString();
  QCOMPARE(type4->declaration(0)->abstractType()->indexed(), type5->indexed());
  QCOMPARE(type3->declaration(top)->internalContext()->scopeIdentifier(true), QualifiedIdentifier("Test"));
  QCOMPARE(type5->declaration(top)->internalContext()->scopeIdentifier(true), QualifiedIdentifier("Test::SubTest"));
  QCOMPARE(type3->declaration(top)->qualifiedIdentifier(), QualifiedIdentifier("Test"));
  QCOMPARE(type5->declaration(top)->qualifiedIdentifier(), QualifiedIdentifier("Test::SubTest"));

  ///@todo think about this
  QCOMPARE(type5->declaration(top)->identifier(), Identifier("SubTest"));
  QVERIFY(subType->equals(type5.unsafeData()));
  QVERIFY(type5->equals(subType.unsafeData()));
  kDebug() << type5->toString() << type4->toString();
  kDebug() << type4->qualifiedIdentifier().toString() << type5->qualifiedIdentifier().toString();
  QVERIFY(type4->equals(type5.unsafeData()));
  QVERIFY(type5->equals(type4.unsafeData()));
  kDebug() << subType->toString();
  QCOMPARE(subType->declaration(0)->abstractType()->indexed(), type5->indexed());

}

void TestDUChain::testForwardDeclaration3()
{
  QByteArray method("namespace B {class Test;} B::Test t; namespace B { class Test {int i; class SubTest; };} B::Test::SubTest t2; namespace B {class Test::SubTest{ int i;};}");

  LockedTopDUContext top = parse(method, DumpNone);

  QCOMPARE(top->localDeclarations().count(), 5);
  QCOMPARE(top->childContexts().count(), 3);

  QVERIFY(dynamic_cast<ForwardDeclaration*>(top->childContexts()[0]->localDeclarations()[0]));

  ForwardDeclaration* forwardDecl = static_cast<ForwardDeclaration*>(top->childContexts()[0]->localDeclarations()[0]);
  kDebug() << forwardDecl->toString();
  QVERIFY(forwardDecl->resolve(0));
  QCOMPARE(forwardDecl->resolve(0), top->childContexts()[1]->localDeclarations()[0]);

  CppClassType::Ptr type1 = top->childContexts()[0]->localDeclarations()[0]->type<CppClassType>();
  CppClassType::Ptr type2 = top->localDeclarations()[1]->type<CppClassType>();
  CppClassType::Ptr type3 = top->childContexts()[1]->localDeclarations()[0]->type<CppClassType>();
  CppClassType::Ptr type4 = top->localDeclarations()[3]->type<CppClassType>();
  CppClassType::Ptr type5 = top->childContexts()[2]->childContexts()[0]->localDeclarations()[0]->type<CppClassType>();

  QCOMPARE(top->childContexts()[2]->localScopeIdentifier(), QualifiedIdentifier("B"));
  QCOMPARE(top->childContexts()[2]->childContexts()[0]->type(), DUContext::Helper);
  QCOMPARE(top->childContexts()[2]->childContexts()[0]->localScopeIdentifier(), QualifiedIdentifier("Test"));



  QVERIFY(type1);
  QVERIFY(type2);
  QVERIFY(type3);
  QVERIFY(type4);
  QVERIFY(type5);

  Declaration* TestDecl = top->childContexts()[1]->localDeclarations()[0];
  QVERIFY(TestDecl->internalContext());
  QCOMPARE(TestDecl->internalContext()->localDeclarations().count(), 2);
  CppClassType::Ptr subType = TestDecl->internalContext()->localDeclarations()[1]->type<CppClassType>();
  QVERIFY(subType);

  QCOMPARE(type1->indexed(), type2->indexed());
  QCOMPARE(type2->declaration(top)->abstractType()->indexed(), type3->indexed());
  QCOMPARE(subType->declaration(top)->abstractType()->indexed(), type4->declaration(top)->abstractType()->indexed());
  kDebug() << type4->declaration(top)->qualifiedIdentifier().toString() << type5->qualifiedIdentifier().toString();
  QCOMPARE(type4->declaration(top)->abstractType()->indexed(), type5->indexed());

}

void TestDUChain::testForwardDeclaration4()
{
  QByteArray method("class Forward;\n"
                    "class Test\n"
                    "{\n"
                    "    virtual Test a(Forward) const=0;\n"
                    "    virtual Forward b(Forward) const=0;\n"
                    "    virtual Forward c(Forward);\n"
                    "    virtual Test d(Forward);\n"
                    "};");

  LockedTopDUContext top = parse(method, DumpNone);

  QCOMPARE(top->localDeclarations().count(), 2);
  QCOMPARE(top->childContexts().count(), 1);

  QVERIFY(dynamic_cast<ForwardDeclaration*>(top->localDeclarations()[0]));

  ForwardDeclaration* forwardDecl = static_cast<ForwardDeclaration*>(top->localDeclarations()[0]);

  QCOMPARE(forwardDecl->uses().size(), 1);
  QCOMPARE(forwardDecl->uses().begin()->size(), 6);
  QCOMPARE(forwardDecl->uses().begin()->at(0), RangeInRevision(3, 19, 3, 26));
  QCOMPARE(forwardDecl->uses().begin()->at(1), RangeInRevision(4, 12, 4, 19));
  QCOMPARE(forwardDecl->uses().begin()->at(2), RangeInRevision(4, 22, 4, 29));
  QCOMPARE(forwardDecl->uses().begin()->at(3), RangeInRevision(5, 12, 5, 19));
  QCOMPARE(forwardDecl->uses().begin()->at(4), RangeInRevision(5, 22, 5, 29));
  QCOMPARE(forwardDecl->uses().begin()->at(5), RangeInRevision(6, 19, 6, 26));

}

void TestDUChain::testTemplateForwardDeclaration()
{
  QByteArray method("class B{}; template<class T = B>class Test; Test<B> t; template<class T = B>class Test {}; ");

  LockedTopDUContext top = parse(method, DumpNone);

  QCOMPARE(top->localDeclarations().count(), 4);

  QVERIFY(top->localDeclarations()[1]->internalContext());

  QVERIFY(dynamic_cast<ForwardDeclaration*>(top->localDeclarations()[1]));

  CppClassType::Ptr type1 = top->localDeclarations()[1]->type<CppClassType>();
  CppClassType::Ptr type2 = top->localDeclarations()[2]->type<CppClassType>();
  CppClassType::Ptr type3 = top->localDeclarations()[3]->type<CppClassType>();

  QVERIFY(type1);
  QVERIFY(type2);
  QVERIFY(type3);

  TemplateDeclaration* temp1Decl = dynamic_cast<TemplateDeclaration*>(type1->declaration(top));
  QVERIFY(temp1Decl);
  TemplateDeclaration* temp2Decl = dynamic_cast<TemplateDeclaration*>(type2->declaration(top));
  QVERIFY(temp2Decl);
  TemplateDeclaration* temp3Decl = dynamic_cast<TemplateDeclaration*>(type3->declaration(top));
  QVERIFY(temp3Decl);

  TemplateDeclaration* temp1DeclResolved = dynamic_cast<TemplateDeclaration*>(type1->declaration(top));
  QVERIFY(temp1DeclResolved);
  TemplateDeclaration* temp2DeclResolved = dynamic_cast<TemplateDeclaration*>(type2->declaration(top));
  QVERIFY(temp2DeclResolved);

  QVERIFY(dynamic_cast<Declaration*>(temp1DeclResolved)->type<CppClassType>());
  QVERIFY(dynamic_cast<Declaration*>(temp2DeclResolved)->type<CppClassType>());

  QCOMPARE(temp2Decl->instantiatedFrom(), temp1Decl);
  QCOMPARE(temp2DeclResolved->instantiatedFrom(), temp1DeclResolved);

  QVERIFY(type2->declaration(0)->abstractType().unsafeData());
  QVERIFY(type2->declaration(0)->type<CppClassType>().unsafeData());
  QCOMPARE(temp2DeclResolved->instantiatedFrom(), temp3Decl);

  QualifiedIdentifier t("Test<>");
  kDebug() << "searching" << t;

  Declaration* decl = findDeclaration(top, QualifiedIdentifier("Test<>"));
  QVERIFY(decl);
  QCOMPARE(decl->abstractType()->toString(), QString("Test< B >"));

}

void TestDUChain::testTemplateForwardDeclaration2()
{
  QByteArray method("class B{}; template<class T = B, class Q = B, class R = B>class Test; Test<B> t; template<class T, class Q, class R>class Test { T a; Q b; R c;}; ");

  LockedTopDUContext top = parse(method, DumpNone);

  QCOMPARE(top->localDeclarations().count(), 4);

  QVERIFY(top->localDeclarations()[1]->internalContext());

  QVERIFY(dynamic_cast<ForwardDeclaration*>(top->localDeclarations()[1]));

  CppClassType::Ptr type1 = top->localDeclarations()[1]->type<CppClassType>();
  CppClassType::Ptr type2 = top->localDeclarations()[2]->type<CppClassType>();
  CppClassType::Ptr type3 = top->localDeclarations()[3]->type<CppClassType>();

  QVERIFY(type1);
  QVERIFY(type2);
  QVERIFY(type3);

  QCOMPARE(type1->declaration(0)->abstractType()->indexed(), type3->indexed());

  TemplateDeclaration* temp1Decl = dynamic_cast<TemplateDeclaration*>(type1->declaration(top));
  QVERIFY(temp1Decl);
  TemplateDeclaration* temp2Decl = dynamic_cast<TemplateDeclaration*>(type2->declaration(top));
  QVERIFY(temp2Decl);
  Declaration* temp2DeclForward = dynamic_cast<Declaration*>(type2->declaration(top));
  QVERIFY(temp2DeclForward);
  TemplateDeclaration* temp3Decl = dynamic_cast<TemplateDeclaration*>(type3->declaration(top));
  QVERIFY(temp3Decl);
  QCOMPARE(temp2Decl->instantiatedFrom(), temp1Decl);
  QCOMPARE(dynamic_cast<TemplateDeclaration*>(temp2DeclForward)->instantiatedFrom(), temp3Decl);

  Declaration* decl = findDeclaration(top, QualifiedIdentifier("Test<>"));
  QVERIFY(decl);
  QVERIFY(decl->internalContext());
  Declaration* aDecl = findDeclaration(decl->logicalInternalContext(0), QualifiedIdentifier("a"));
  QVERIFY(aDecl);
  Declaration* bDecl = findDeclaration(decl->logicalInternalContext(0), QualifiedIdentifier("b"));
  QVERIFY(bDecl);
  Declaration* cDecl = findDeclaration(decl->logicalInternalContext(0), QualifiedIdentifier("c"));
  QVERIFY(cDecl);

}

void TestDUChain::testUses()
{
  QByteArray method(
    "class GenericArgument\n"
    "{\n"
    "  public:\n"
    "    GenericArgument() {}\n"
    "    GenericArgument( const char*, const void* ) {}\n"
    "};\n"
    "\n"
    "template <class T>\n"
    "class Argument : public GenericArgument\n"
    "{\n"
    "  public:\n"
    "    Argument( const char* name, const T &value )\n"
    "      : GenericArgument( name, static_cast<const void*>( &value ) )\n"
    "    {}\n"
    "};\n"
    "\n"
    "class A\n"
    "{\n"
    "  public:\n"
    "    A()\n"
    "    {\n"
    "      A::invokeMethod( this, \"hello\", Argument<bool>( \"test\", true ) );\n"
    "    }\n"
    "\n"
    "    void invokeMethod( A*, const char*, int foo = 0, const GenericArgument &arg = GenericArgument() ) {}\n"
    "    void invokeMethod( A*, const char*, const GenericArgument& ) {}\n"
    "};\n"
  );

  LockedTopDUContext top = parse(method, DumpAll);

  QCOMPARE(top->childContexts().size(), 4);
  QCOMPARE(top->childContexts().at(0)->type(), DUContext::Class);
  QCOMPARE(top->childContexts().at(1)->type(), DUContext::Template);
  QCOMPARE(top->childContexts().at(2)->type(), DUContext::Class);
  QCOMPARE(top->childContexts().at(3)->type(), DUContext::Class);

  QList<Declaration*> decls = top->findDeclarations(QualifiedIdentifier("A::invokeMethod"));
  QCOMPARE(decls.count(), 2);

  // only declaration 2 is used, check this now
  QCOMPARE(decls.at( 0 )->uses().count(), 0);
  QCOMPARE(decls.at( 1 )->uses().count(), 1);

}

void TestDUChain::testCtorTypes()
{
  QByteArray method(
    "class A\n"
    "{\n"
    "  public:\n"
    "    A() {}\n"
    "    A( int ) {}\n"
    "    A( int* ) {}\n"
    "    A( int, int ) {}\n"
    "};\n"
  );
  LockedTopDUContext top = parse(method, DumpAll);

  QVector< Declaration* > ctors = top->childContexts().first()->localDeclarations();

  QList< QPair<IndexedType, IndexedString> > encountered;
  QMap< QPair<IndexedType, IndexedString>, int > encountered2;

  for ( int i = 0; i < ctors.size(); ++i ) {
    for ( int j = i + 1; j < ctors.size(); ++j ) {
      kDebug() << "comparing type of " << ctors[i]->toString() << "with" << ctors[j]->toString();
      QVERIFY(ctors[i]->indexedType() != ctors[j]->indexedType());
    }
    QList< Declaration* > decs = top->childContexts().first()->findLocalDeclarations(
        ctors[i]->identifier(), CursorInRevision::invalid(), 0, ctors[i]->abstractType(), DUContext::OnlyFunctions);
    QCOMPARE(decs.count(), 1);

    QPair<IndexedType, IndexedString> key = qMakePair(ctors[i]->indexedType(), ctors[i]->identifier().identifier());
    QVERIFY(!encountered.contains(key));
    encountered << key;

    // everything works, but this fails:
    QVERIFY(!encountered2.contains(key));
    encountered2.insert(key, 0);
  }
}

void TestDUChain::testConst()
{
  {
    QByteArray method("class A{}; const char* a; const char& b; char* const & c; char* const d; const A e; const A* f; A* const g;");

    LockedTopDUContext top = parse(method, DumpNone);

    QCOMPARE(top->localDeclarations().size(), 8);
    PointerType::Ptr a = top->localDeclarations()[1]->type<PointerType>();
    QVERIFY(a);
    QVERIFY(!(a->modifiers() & AbstractType::ConstModifier));
    IntegralType::Ptr a2 = a->baseType().cast<IntegralType>();
    QVERIFY(a2);
    QVERIFY(a2->modifiers() & AbstractType::ConstModifier);

    ReferenceType::Ptr b = top->localDeclarations()[2]->type<ReferenceType>();
    QVERIFY(b);
    QVERIFY(!(b->modifiers() & AbstractType::ConstModifier));
    IntegralType::Ptr b2 = b->baseType().cast<IntegralType>();
    QVERIFY(b2);
    QVERIFY(b2->modifiers() & AbstractType::ConstModifier);

    ReferenceType::Ptr c = top->localDeclarations()[3]->type<ReferenceType>();
    QVERIFY(c);
    QVERIFY(!(c->modifiers() & AbstractType::ConstModifier));
    PointerType::Ptr c2 = c->baseType().cast<PointerType>();
    QVERIFY(c2);
    QVERIFY(c2->modifiers() & AbstractType::ConstModifier);
    IntegralType::Ptr c3 = c2->baseType().cast<IntegralType>();
    QVERIFY(c3);
    QVERIFY(!(c3->modifiers() & AbstractType::ConstModifier));

    PointerType::Ptr d = top->localDeclarations()[4]->type<PointerType>();
    QVERIFY(d);
    QVERIFY(d->modifiers() & AbstractType::ConstModifier);
    IntegralType::Ptr d2 = d->baseType().cast<IntegralType>();
    QVERIFY(d2);
    QVERIFY(!(d2->modifiers() & AbstractType::ConstModifier));

    CppClassType::Ptr e = top->localDeclarations()[5]->type<CppClassType>();
    QVERIFY(e);
    QVERIFY(e->modifiers() & AbstractType::ConstModifier);

    PointerType::Ptr f = top->localDeclarations()[6]->type<PointerType>();
    QVERIFY(f);
    QVERIFY(!(f->modifiers() & AbstractType::ConstModifier));
    CppClassType::Ptr f2 = f->baseType().cast<CppClassType>();
    QVERIFY(f2);
    QVERIFY(f2->modifiers() & AbstractType::ConstModifier);

    PointerType::Ptr g = top->localDeclarations()[7]->type<PointerType>();
    QVERIFY(g);
    QVERIFY(g->modifiers() & AbstractType::ConstModifier);
    CppClassType::Ptr g2 = g->baseType().cast<CppClassType>();
    QVERIFY(g2);
    QVERIFY(!(g2->modifiers() & AbstractType::ConstModifier));
  }
  {
    QByteArray method("class A; template<class T> class B; B<const A*> ca;B<A* const> cb;");

    LockedTopDUContext top = parse(method, DumpNone);

    QCOMPARE(top->localDeclarations().size(), 4);
    QCOMPARE(top->localDeclarations()[2]->abstractType()->toString().trimmed(), QString("B< const A* >"));
    QCOMPARE(top->localDeclarations()[3]->abstractType()->toString().trimmed(), QString("B< A* const >"));
  }
  {
    QByteArray method("class A; A* a = const_cast<A*>(bla);");

    LockedTopDUContext top = parse(method, DumpNone);

    QCOMPARE(top->localDeclarations().size(), 2);
  }
  {
    QByteArray method("class C;const C& c;");

    LockedTopDUContext top = parse(method, DumpNone);

    QCOMPARE(top->localDeclarations().size(), 2);
    QVERIFY(top->localDeclarations()[1]->type<ReferenceType>());
    QVERIFY(top->localDeclarations()[1]->type<ReferenceType>()->baseType());
    QVERIFY(top->localDeclarations()[1]->type<ReferenceType>()->baseType()->modifiers() & AbstractType::ConstModifier);
    kDebug() << top->localDeclarations()[1]->type<ReferenceType>()->toString();
  }}

void TestDUChain::testDeclarationId()
{
  QByteArray method("template<class T> class C { template<class T2> class C2{}; }; ");

  LockedTopDUContext top = parse(method, DumpNone);

  QVERIFY(top->inSymbolTable());
  QCOMPARE(top->localDeclarations().count(), 1);
  QVERIFY(top->localDeclarations()[0]->internalContext());
  QVERIFY(top->localDeclarations()[0]->inSymbolTable());
  QCOMPARE(top->localDeclarations()[0]->internalContext()->localDeclarations().count(), 1);

  QCOMPARE(top->childContexts().count(), 2);
  QCOMPARE(top->childContexts()[1]->localDeclarations().count(), 1);
  QVERIFY(top->childContexts()[1]->localDeclarations()[0]->inSymbolTable());
  //kDebug() << "pointer of C2:" << top->childContexts()[1]->localDeclarations()[0];
  Declaration* decl = findDeclaration(top, QualifiedIdentifier("C<int>::C2<float>"));
  QVERIFY(decl);
  kDebug() << decl->toString();
  kDebug() << decl->qualifiedIdentifier().toString();
  DeclarationId id = decl->id();
  QVERIFY(top->localDeclarations()[0]->internalContext());
//   kDebug() << "id:" << id.m_direct << id.m_specialization << "indirect:" << id.indirect.m_identifier.index << id.indirect.m_additionalIdentity << "direct:" << *((uint*)(&id.direct)) << *(((uint*)(&id.direct))+1);
  Declaration* declAgain = id.getDeclaration(top);
  QVERIFY(!id.isDirect());
  QVERIFY(declAgain);
  QCOMPARE(declAgain->qualifiedIdentifier().toString(), decl->qualifiedIdentifier().toString());
  QCOMPARE(declAgain, decl);
  //kDebug() << declAgain->qualifiedIdentifier().toString();
  //kDebug() << declAgain->toString();
  QCOMPARE(declAgain, decl);

}

void TestDUChain::testFileParse()
{
  //QSKIP("Unwanted", SkipSingle);

  //QFile file("/opt/kde4/src/kdevelop/languages/cpp/duchain/tests/files/membervariable.cpp");
  QFile file("/opt/kde4/src/kdevelop/languages/csharp/parser/csharp_parser.cpp");
  //QFile file("/opt/kde4/src/kdevelop/plugins/outputviews/makewidget.cpp");
  //QFile file("/opt/kde4/src/kdelibs/kate/part/katecompletionmodel.h");

  //QFile file("/opt/kde4/src/kdevelop/lib/kdevbackgroundparser.cpp");
  //QFile file("/opt/qt-copy/src/gui/kernel/qwidget.cpp");
  QVERIFY( file.open( QIODevice::ReadOnly ) );

  QByteArray fileData = file.readAll();
  file.close();
  QString contents = QString::fromUtf8( fileData.constData() );
  rpp::Preprocessor preprocessor;
  rpp::pp pp(&preprocessor);

  QByteArray preprocessed = stringFromContents(pp.processFile("anonymous", fileData));

  LockedTopDUContext top = parse(preprocessed, DumpNone);

}

void TestDUChain::testAutoTypeIntegral_data()
{
  QTest::addColumn<QString>("code");
  QTest::addColumn<uint>("datatype");
  QTest::addColumn<bool>("constness");
  QTest::addColumn<QString>("string");

  QTest::newRow("int") << "auto x = 1;" << (uint) IntegralType::TypeInt << false << "int";
  QTest::newRow("double") << "auto x = 1.0;" << (uint) IntegralType::TypeDouble << false << "double";
  QTest::newRow("bool") << "auto x = false;" << (uint) IntegralType::TypeBoolean << false << "bool";
  QTest::newRow("const-int-var") << "int a = 1; const auto x = a;" << (uint) IntegralType::TypeInt << true << "const int";
}

void TestDUChain::testAutoTypeIntegral()
{
  QFETCH(QString, code);
  QFETCH(uint, datatype);
  QFETCH(bool, constness);
  QFETCH(QString, string);

  LockedTopDUContext top = parse(code.toLocal8Bit(), DumpAll);
  QVERIFY(top);
  DUChainReadLocker lock;
  QVERIFY(top->problems().isEmpty());

  QVERIFY(top->localDeclarations().count() >= 1);
  Declaration* dec = top->findLocalDeclarations(Identifier("x")).first();
  QVERIFY(dec);
  IntegralType::Ptr type = dec->abstractType().cast<IntegralType>();
  QVERIFY(type);
  QCOMPARE(type->dataType(), datatype);
  QCOMPARE((bool) (type->modifiers() & AbstractType::ConstModifier), constness);
  QCOMPARE(type->toString(), string);
}

void TestDUChain::testAutoTypes()
{
  LockedTopDUContext top = parse("struct Foo{}; int main() {\n"
                                 "  auto v1 = new Foo;"
                                 "  auto v2 = Foo;"
                                 "  int tmp1 = 0; auto v3 = &tmp1;"
// TODO: support this, when including <initializer_list>
//                                  "  auto v4 = {1,2};"
//                                  "  auto v5 = {1.0,2.0};"
                                 "}\n", DumpAll);
  QVERIFY(top);
  DUChainReadLocker lock;
  QVERIFY(top->problems().isEmpty());

  DUContext* ctx = top->childContexts().last();
  {
    Declaration* dec = ctx->findDeclarations(Identifier("v1")).first();
    PointerType::Ptr type = dec->abstractType().cast<PointerType>();
    QVERIFY(type);
    StructureType::Ptr structType = type->baseType().cast<StructureType>();
    QVERIFY(structType);
    QCOMPARE(structType->qualifiedIdentifier().toString(), QString("Foo"));
  }
  {
    Declaration* dec = ctx->findDeclarations(Identifier("v2")).first();
    StructureType::Ptr type = dec->abstractType().cast<StructureType>();
    QEXPECT_FAIL("", "It's a delayed type, not a structure type...", Continue);
    QVERIFY(type);
//TODO:     QCOMPARE(type->qualifiedIdentifier().toString(), QString("Foo"));
  }
  {
    Declaration* dec = ctx->findDeclarations(Identifier("v3")).first();
    PointerType::Ptr type = dec->abstractType().cast<PointerType>();
    QVERIFY(type);
    IntegralType::Ptr intType = type->baseType().cast<IntegralType>();
    QVERIFY(intType);
    QCOMPARE(intType->dataType(), (uint) IntegralType::TypeInt);
  }
}

void TestDUChain::testCommentAfterFunctionCall()
{
  // testcase for bug https://bugs.kde.org/show_bug.cgi?id=241793
  
  LockedTopDUContext top = parse("class View;\n"
                                 "void setView(View* m_view) {\n"
                                 "  setView(0);\n"
                                 "  setView(m_view); //\n"
                                 "}\n", DumpAll);
  QVERIFY(top);
  DUChainReadLocker lock;
  QVERIFY(top->problems().isEmpty());

  QCOMPARE(top->localDeclarations().size(), 2);
  QCOMPARE(top->childContexts().size(), 2);

  QCOMPARE(top->childContexts().first()->localDeclarations().size(), 1);
  Declaration* m_view = top->childContexts().first()->localDeclarations().first();

  QVERIFY(top->childContexts().last()->localDeclarations().isEmpty());
  QCOMPARE(m_view->uses().size(), 1);
}

void TestDUChain::testNestedNamespace()
{
  // see bug: https://bugs.kde.org/show_bug.cgi?id=273658

  //                                        1         2         3
  //                               123456789012345678901234567890
  LockedTopDUContext top = parse( "namespace v8 {\n"
                                  "  struct Isolate { int bar(); };\n"
                                  "  namespace internal {\n"
                                  "    struct Isolate { int foo(); };\n"
                                  "    void function() {\n"
                                  "      Isolate a;\n"   // this should be v8::internal::Isolate, but kdevelop thinks it is v8::Isolate
                                  "      a.foo();\n"
                                  "    }\n"
                                  "  }\n"
                                  "}\n", DumpNone );
  QVERIFY(top);
  DUChainReadLocker lock;
  QVERIFY(top->problems().isEmpty());

  // namespace v8
  QCOMPARE(top->childContexts().size(), 1);
  QCOMPARE(top->localDeclarations().size(), 1);

  DUContext* v8Ctx = top->childContexts().at(0);
  QCOMPARE(v8Ctx->localDeclarations().size(), 2);

  Declaration* v8IsolateDec = v8Ctx->localDeclarations().first();
  QCOMPARE(v8IsolateDec->qualifiedIdentifier().toString(), QString("v8::Isolate"));

  DUContext* v8InternalCtx = v8Ctx->childContexts().last();
  QCOMPARE(v8InternalCtx->localDeclarations().size(), 2);

  Declaration* v8InternalIsolateDec = v8InternalCtx->localDeclarations().first();
  QCOMPARE(v8InternalIsolateDec->qualifiedIdentifier().toString(), QString("v8::internal::Isolate"));

  DUContext* v8InternalFunctionCtx = v8InternalCtx->childContexts().last();
  QCOMPARE(v8InternalFunctionCtx->localDeclarations().size(), 1);

  Declaration* aDec = v8InternalFunctionCtx->localDeclarations().first();
  QVERIFY(aDec->abstractType()->equals(v8InternalIsolateDec->abstractType().constData()));

  QEXPECT_FAIL("", "uses are still associated incorreclty, probably to be fixed in TypeASTVisitor::visitSimpleTypeSpecifier", Abort);
  QVERIFY(v8IsolateDec->uses().isEmpty());
  QVERIFY(!v8InternalIsolateDec->uses().isEmpty());
  QCOMPARE(v8InternalIsolateDec->uses().size(), 1);
  QCOMPARE(v8InternalIsolateDec->uses().begin()->size(), 1);
  QCOMPARE(v8InternalIsolateDec->uses().begin()->first().start.line, 5);
}

void TestDUChain::testPointerToMember()
{
  //                                         1         2         3
  //                                123456789012345678901234567890
  LockedTopDUContext top = parse(  "struct AA {"
                                 "\n  int j;"
                                 "\n};"
                                 "\nstruct BB{"
                                 "\n  int AA::* pj;"
                                 "\n};"
                                 "\nvoid f(int AA::* par){"
                                 "\n  BB b;"
                                 "\n  int AA::* BB::* ppj=&BB::pj;"
                                 "\n  b.*ppj=par;"
                                 "\n}",DumpAll
                           );
  QVERIFY(top);
  DUChainReadLocker lock;
  QVERIFY(top->problems().isEmpty());
  QCOMPARE(top->childContexts().size(),4);
  QCOMPARE(top->localDeclarations().size(),3);

  QCOMPARE(top->localDeclarations().at(0)->uses().begin()->size(),3);
  QCOMPARE(top->localDeclarations().at(1)->uses().begin()->size(),3);

  QCOMPARE(top->localDeclarations().at(0)->uses().begin()->at(0),RangeInRevision(4,6,4,8));
  QCOMPARE(top->localDeclarations().at(0)->uses().begin()->at(1),RangeInRevision(6,11,6,13));
  QCOMPARE(top->localDeclarations().at(0)->uses().begin()->at(2),RangeInRevision(8,6,8,8));

  QCOMPARE(top->localDeclarations().at(1)->uses().begin()->at(0),RangeInRevision(7,2,7,4));
  QCOMPARE(top->localDeclarations().at(1)->uses().begin()->at(1),RangeInRevision(8,12,8,14));
  QCOMPARE(top->localDeclarations().at(1)->uses().begin()->at(2),RangeInRevision(8,23,8,25));

  {
    DUContext* ctx = top->childContexts().at(1);
    Declaration* dec = ctx->localDeclarations().first();
    PtrToMemberType::Ptr type = dec->abstractType().cast<PtrToMemberType>();
    QVERIFY(type);
    StructureType::Ptr structType = type->classType().cast<StructureType>();
    QVERIFY(structType);
    QCOMPARE(structType->qualifiedIdentifier().toString(), QString("AA"));
  }
  {
    DUContext* ctx = top->childContexts().at(3);
    Declaration* dec = ctx->localDeclarations().at(1);
    PtrToMemberType::Ptr type = dec->abstractType().cast<PtrToMemberType>();

    {
      QVERIFY(type);
      StructureType::Ptr structType = type->classType().cast<StructureType>();
      QVERIFY(structType);
      QCOMPARE(structType->qualifiedIdentifier().toString(), QString("BB"));
    }

    PtrToMemberType::Ptr ptype = type->baseType().cast<PtrToMemberType>();
    {
      QVERIFY(ptype);
      StructureType::Ptr structType = ptype->classType().cast<StructureType>();
      QVERIFY(structType);
      QCOMPARE(structType->qualifiedIdentifier().toString(), QString("AA"));
    }
  }

}

void TestDUChain::testMemberPtrCrash()
{
  QByteArray code(
  "template<typename T>\n"
  "class A{};\n"
  "class B{};\n"
  "class C{};\n"
  "A<B C::*> x;\n");
  LockedTopDUContext top = parse(code, DumpNone);
}

void TestDUChain::testDeclarationHasUses()
{
  QByteArray method(
    "int a,b,c,d;\n"
    "void f() {"
    "  if(a) {"
    "    b++;"
    "  }"
    "}"
  );
  LockedTopDUContext top = parse(method, DumpAll);

  QCOMPARE(top->childContexts().first()->findDeclarations(KDevelop::QualifiedIdentifier("a")).first()->hasUses(), true);
  QCOMPARE(top->childContexts().first()->findDeclarations(KDevelop::QualifiedIdentifier("b")).first()->hasUses(), true);
  QCOMPARE(top->childContexts().first()->findDeclarations(KDevelop::QualifiedIdentifier("c")).first()->hasUses(), false);
  QCOMPARE(top->childContexts().first()->findDeclarations(KDevelop::QualifiedIdentifier("d")).first()->hasUses(), false);
}

void TestDUChain::testInitListRegressions()
{
  {
  // as see in /usr/include/c++/4.6.1/bits/stl_bvector.h
    QByteArray code(
      "#define __CHAR_BIT__ 8\n"
      "typedef unsigned long _Bit_type;\n"
      "enum { _S_word_bit = int(__CHAR_BIT__ * sizeof(_Bit_type)) };\n"
    );

    LockedTopDUContext top = parse(code, DumpAll);
    QVERIFY(top);
    QVERIFY(top->problems().isEmpty());
  }
}
void TestDUChain::testBug269352()
{
  // see also: https://bugs.kde.org/show_bug.cgi?id=269352
  QByteArray code(
    "class X {}; class Y {};\n"
    "template <typename> class A;\n"
    "template <typename, typename> struct B;\n"
    "template <typename T1>\n"
    "struct B<T1, Y> : B<A<T1>, X> {};\n"
    // crucial: same typename like class above above
    "template <typename A> struct B<A, X> {};\n"
  );

  LockedTopDUContext top = parse(code, DumpNone);
  QVERIFY(top);
  QVERIFY(top->problems().isEmpty());
  Declaration *structB = top->localDeclarations()[3];
  TemplateDeclaration *structBTemplate = dynamic_cast<TemplateDeclaration*>(structB);
  QVERIFY(structBTemplate);
  QEXPECT_FAIL("", "There should be zero instantiations, only the specializations", Continue);
  QVERIFY(structBTemplate->instantiations().size() == 0);
  //For now, at least there shouldn't be more than 1
  QVERIFY(structBTemplate->instantiations().size() < 2);
  QVERIFY(structBTemplate->specializationsSize() == 2);
  Declaration *structBSpec = top->localDeclarations()[4];
  TemplateDeclaration *structBSpecTemplate = dynamic_cast<TemplateDeclaration*>(structBSpec);
  QVERIFY(structBSpecTemplate);
  QVERIFY(structBSpecTemplate->instantiations().size() == 0);
  QVERIFY(structBSpecTemplate->specializationsSize() == 0);
  Declaration *structBSpec2 = top->localDeclarations()[5];
  TemplateDeclaration *structBSpecTemplate2 = dynamic_cast<TemplateDeclaration*>(structBSpec2);
  QVERIFY(structBSpecTemplate2);
  QVERIFY(structBSpecTemplate2->instantiations().size() == 0);
  QVERIFY(structBSpecTemplate2->specializationsSize() == 0);
}

void TestDUChain::testRenameClass()
{
  QByteArray codeBefore(
    "class A { enum { Foo = 1 }; };"
  );

  TopDUContext* top = parse(codeBefore, DumpDUChain);
  {
    DUChainReadLocker lock;
    QVERIFY(top);
    QVERIFY(top->problems().isEmpty());
  }

  QByteArray codeAfter(
    "class B { enum { Foo = 1 }; };"
  );

  parse(codeAfter, DumpDUChain, top);
  DUChainReadLocker lock;
  QVERIFY(top);
  QVERIFY(top->problems().isEmpty());
  QCOMPARE(top->localDeclarations().size(), 1);
  QCOMPARE(top->localDeclarations().first()->identifier().toString(), QString("B"));
  QCOMPARE(top->childContexts().size(), 1);
  QCOMPARE(top->childContexts().first()->localScopeIdentifier().toString(), QString("B"));
}

void TestDUChain::testQProperty()
{
  LockedTopDUContext top = parse(
    "class QString {};\n"
    "class A {\n"
    "  __qt_property__(QString a READ a WRITE setA NOTIFY aChanged)\n"
    "public:\n"
    "  QString a();\n"
    "  void setA(const QString&);\n"
    "__qt_signals__:\n"
    "  void aChanged(QString);\n"
    "};\n");

  QVERIFY(top);
  QVERIFY(top->problems().isEmpty());
  QCOMPARE(top->localDeclarations().size(), 2);
  QCOMPARE(top->childContexts().size(), 2);
  Declaration* QStringDecl = top->localDeclarations().first();
  QCOMPARE(QStringDecl->uses().size(), 1);
  QCOMPARE(QStringDecl->uses().begin()->size(), 4);
  QCOMPARE(QStringDecl->uses().begin()->first(), RangeInRevision(2, 18, 2, 25));
  QVector<Declaration*> decls = top->childContexts().last()->localDeclarations();
  QCOMPARE(decls.size(), 3);
  QCOMPARE(decls.at(0)->toString(), QLatin1String("QString a ()"));
  QCOMPARE(decls.at(0)->uses().size(), 1);
  QCOMPARE(decls.at(0)->uses().begin()->size(), 1);
  QCOMPARE(decls.at(0)->uses().begin()->first(), RangeInRevision(2, 33, 2, 34));
  QCOMPARE(decls.at(1)->toString(), QLatin1String("void setA (const QString&)"));
  QCOMPARE(decls.at(1)->uses().size(), 1);
  QCOMPARE(decls.at(1)->uses().begin()->size(), 1);
  QCOMPARE(decls.at(1)->uses().begin()->first(), RangeInRevision(2, 41, 2, 45));
  QCOMPARE(decls.at(2)->toString(), QLatin1String("void aChanged (QString)"));
  QCOMPARE(decls.at(2)->uses().size(), 1);
  QCOMPARE(decls.at(2)->uses().begin()->size(), 1);
  QCOMPARE(decls.at(2)->uses().begin()->first(), RangeInRevision(2, 53, 2, 61));
}

#include "test_duchain.moc"
