/* This file is part of KDevelop Copyright 2006 Hamish Rodda Copyright 2007-2009 David Nolden 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 #include "declarationbuilder.h" #include "usebuilder.h" #include "cpptypes.h" #include "templateparameterdeclaration.h" #include "dumptypes.h" #include "typeutils.h" #include "templatedeclaration.h" #include "qtfunctiondeclaration.h" #include "sourcemanipulation.h" #include "ptrtomembertype.h" #include "overloadresolution.h" #include "rpp/chartools.h" #include "rpp/pp-engine.h" #include "rpp/preprocessor.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KTextEditor; using namespace TypeUtils; using namespace KDevelop; using namespace Cpp; using namespace Utils; QTEST_MAIN(TestDUChain) #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 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 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::jump ::tes"); 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::jump ::tes").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()->arguments().count(), 2); QCOMPARE(defInfinity->type()->arguments()[1]->toString(), QString("...")); } void TestDUChain::testEllipsisVexing() { QByteArray method("void infinity(...);"); LockedTopDUContext top = parse(method, DumpNone); Declaration* defInfinity = top->localDeclarations().first(); QCOMPARE(defInfinity->type()->arguments().count(), 1); QCOMPARE(defInfinity->type()->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(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(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(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()); QCOMPARE(defI->type()->dataType(), (uint)IntegralType::TypeInt); QCOMPARE(defI->type()->modifiers(), (unsigned long long)(AbstractType::UnsignedModifier | AbstractType::ConstModifier)); Declaration* defK = top->localDeclarations()[1]; QCOMPARE(defK->identifier(), Identifier("k")); QCOMPARE(defK->type()->indexed(), defI->type()->indexed()); Declaration* defJ = top->localDeclarations()[2]; QCOMPARE(defJ->identifier(), Identifier("j")); QCOMPARE(findDeclaration(top, defJ->identifier()), defJ); QVERIFY(defJ->type()); QCOMPARE(defJ->type()->dataType(), (uint)IntegralType::TypeDouble); QCOMPARE(defJ->type()->modifiers(), (unsigned long long)AbstractType::LongModifier | (unsigned long long)AbstractType::VolatileModifier); Declaration* defL = top->localDeclarations()[3]; QCOMPARE(defL->identifier(), Identifier("l")); QVERIFY(defL->type()); QCOMPARE(defL->type()->baseType()->indexed(), typeInt); QCOMPARE(defL->type()->modifiers(), (unsigned long long)AbstractType::NoModifiers); Declaration* defM = top->localDeclarations()[4]; QCOMPARE(defM->identifier(), Identifier("m")); PointerType::Ptr firstpointer = defM->type(); 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()); base = IntegralType::Ptr::dynamicCast(defN->type()->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()); QCOMPARE(defO->type()->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()); QCOMPARE(defP->type()->modifiers(), defO->type()->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(); QVERIFY(funType); QVERIFY(funType->returnType().cast()); } 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()); Declaration* defI = top->localDeclarations().last(); QCOMPARE(defI->identifier(), Identifier("i")); QCOMPARE(findDeclaration(top, defI->identifier()), defI); ArrayType::Ptr array = defI->type(); 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 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::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 class A{ A(){} };\n" "template class B : public A { B() : A() {} };"); 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 QCOMPARE(top->localDeclarations().first()->uses().begin()->at(0), RangeInRevision(1, 38, 1, 39)); // use in B() : A() 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()); QCOMPARE(defMain->type()->returnType()->indexed(), typeInt); QCOMPARE(defMain->type()->arguments().count(), 0); QCOMPARE(defMain->type()->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(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(t.unsafeData())); EnumerationType* en = static_cast(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(t.unsafeData())); EnumeratorType* en = static_cast(t.unsafeData()); QCOMPARE((int)en->value(), 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(t.unsafeData())); en = static_cast(t.unsafeData()); QCOMPARE((int)en->value(), 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(t.unsafeData())); en = static_cast(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(t.unsafeData())); EnumeratorType* en = static_cast(t.unsafeData()); QCOMPARE((int)en->value(), 0); QCOMPARE(en->declaration(top)->context()->owner(), Enum2Decl); decl = findDeclaration(top, Identifier("value22")); QVERIFY(decl); t = decl->abstractType(); QVERIFY(dynamic_cast(t.unsafeData())); en = static_cast(t.unsafeData()); QCOMPARE((int)en->value(), 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(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(member); \ QVERIFY(memberFun1); \ member = clazzCtx->localDeclarations()[1]; \ memberFun2 = dynamic_cast(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(); 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(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(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()); // QVERIFY(defStructA->internalContext()); // QCOMPARE(defStructA->internalContext()->localDeclarations().count(), 1); // Cpp::ClassDeclaration* classDecl = dynamic_cast(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(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()); QVERIFY(defStructA->internalContext()); QCOMPARE(defStructA->internalContext()->localDeclarations().count(), 1); ClassDeclaration* classDecl = dynamic_cast(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(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(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()); ClassDeclaration* classDecl = dynamic_cast(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(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()); ClassDeclaration* classDecl = dynamic_cast(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(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(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()); 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(); 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()); 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()); 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::Ptr innerA = top->childContexts()[1]->localDeclarations()[0]->abstractType().cast(); QVERIFY(outerA); QVERIFY(innerA); Declaration* klassDecl = innerA->declaration(top); ClassDeclaration* cppClassDecl = dynamic_cast(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()); } 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()); 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()); } 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(); 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(); 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(); 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(); 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(); 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(); 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(); 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(); 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 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 struct B { }; template void bar(T &) {} }" "int test() { foo::B a; bar(a); }"); // calls foo::bar LockedTopDUContext top( parse(adlCall, DumpNone) ); Declaration* d = findDeclaration(top, QualifiedIdentifier("foo::bar >")); QVERIFY(d); QCOMPARE(d->uses().size(), 1); QCOMPARE(d->uses().begin()->size(), 1); } { QByteArray adlCall("template struct B { };" "namespace foo { struct A { }; template void bar(T &) {} }" "int test() { B a; bar(a); }"); // calls foo::bar LockedTopDUContext top( parse(adlCall, DumpNone) ); Declaration* d = findDeclaration(top, QualifiedIdentifier("foo::bar >")); QVERIFY(d); QCOMPARE(d->uses().size(), 1); QCOMPARE(d->uses().begin()->size(), 1); } { QByteArray adlCall("template struct B { };" "namespace foo { struct A { }; template void bar(T &) {} }" "int test() { B > a; bar(a); }"); // calls foo::bar LockedTopDUContext top( parse(adlCall, DumpNone) ); Declaration* d = findDeclaration(top, QualifiedIdentifier("foo::bar > >")); QVERIFY(d); QCOMPARE(d->uses().size(), 1); QCOMPARE(d->uses().begin()->size(), 1); } { QByteArray adlCall("template struct B { };" "namespace foo { struct A { }; template void bar(T &) {} }" "int test() { B a; bar(a); }"); // calls foo::bar LockedTopDUContext top( parse(adlCall, DumpNone) ); Declaration* d = findDeclaration(top, QualifiedIdentifier("foo::bar >")); QVERIFY(d); QCOMPARE(d->uses().size(), 1); QCOMPARE(d->uses().begin()->size(), 1); } { QByteArray adlCall("namespace foo { enum E { value }; template void bar(T &) {} }" "template struct B { };" "int test() { B a; bar(a); }"); LockedTopDUContext top( parse(adlCall, DumpNone) ); Declaration* d = findDeclaration(top, QualifiedIdentifier("foo::bar >")); QVERIFY(d); QCOMPARE(d->uses().size(), 1); QCOMPARE(d->uses().begin()->size(), 1); } { QByteArray nonAdlCall("namespace foo { enum E { value }; template void bar(T &) {} }" "template struct B { };" "int test() { B a; bar(a); }"); LockedTopDUContext top( parse(nonAdlCall, DumpNone) ); Declaration* d = findDeclaration(top, QualifiedIdentifier("foo::bar >")); QVERIFY(d); QCOMPARE(d->uses().size(), 0); } { QByteArray nonAdlCall("namespace foo { struct A {}; void f(A& a) {} template void bar(T &) {} }" "template struct B { };" "int test() { B<&foo::f> a; bar(a); }"); LockedTopDUContext top( parse(nonAdlCall, DumpNone) ); Declaration* d = findDeclaration(top, QualifiedIdentifier("foo::bar >")); QVERIFY(d); QCOMPARE(d->uses().size(), 0); } } void TestDUChain::testADLTemplateTemplateArguments() { { QByteArray adlCall("namespace foo { struct A {}; template void bar(T &) {} }" "struct C : public foo::A { };" // foo::A is associated class of C "template struct B { };" "int test() { B a; bar(a); }"); // in this ADL call LockedTopDUContext top( parse(adlCall, DumpNone) ); Declaration* d = findDeclaration(top, QualifiedIdentifier("foo::bar >")); QVERIFY(d); QCOMPARE(d->uses().size(), 1); QCOMPARE(d->uses().begin()->size(), 1); } { QByteArray nonAdlCall("namespace foo { struct A {}; template void bar(T &) {} }" "template struct C : public foo::A { };" // foo::A is not an associated class of C ... "template class T> struct B { };" "int test() { B a; bar(a); }"); // in this ADL call LockedTopDUContext top( parse(nonAdlCall, DumpNone) ); Declaration* d = findDeclaration(top, QualifiedIdentifier("foo::bar >")); 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 A { T i; }; template struct B: private A { using A::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::i")); QVERIFY(decl); QVERIFY(decl->abstractType()); QVERIFY(decl->type()); } 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(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(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(top->localDeclarations()[1]); QVERIFY(aliasDecl1); QCOMPARE(aliasDecl1->importIdentifier(), QualifiedIdentifier("foo")); QCOMPARE(aliasDecl1->identifier(), Identifier("A")); NamespaceAliasDeclaration* aliasDecl2 = dynamic_cast(top->localDeclarations()[2]); QVERIFY(aliasDecl2); QCOMPARE(aliasDecl2->importIdentifier(), QualifiedIdentifier("foo")); // already resolved QCOMPARE(aliasDecl2->identifier(), Identifier("B")); NamespaceAliasDeclaration* aliasDecl3 = dynamic_cast(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(top->localDeclarations()[1]); QVERIFY(aliasDecl1); QCOMPARE(aliasDecl1->importIdentifier(), QualifiedIdentifier("foo")); QCOMPARE(aliasDecl1->identifier(), Identifier("A")); NamespaceAliasDeclaration* aliasDecl2 = dynamic_cast(top->localDeclarations()[2]); QVERIFY(aliasDecl2); QCOMPARE(aliasDecl2->importIdentifier(), QualifiedIdentifier("foo")); // already resolved QCOMPARE(aliasDecl2->identifier(), Identifier("B")); NamespaceAliasDeclaration* aliasDecl3 = dynamic_cast(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(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(top->childContexts()[0]->localDeclarations()[0]); QVERIFY(classFun); QVERIFY(classFun->accessPolicy() == ClassMemberDeclaration::Protected); QVERIFY(classFun->isSignal()); classFun = dynamic_cast(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(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(top->childContexts()[0]->localDeclarations()[0]); QVERIFY(classFun); QVERIFY(classFun->accessPolicy() == ClassMemberDeclaration::Protected); QVERIFY(classFun->isSignal()); classFun = dynamic_cast(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(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(top->localDeclarations()[1]); QVERIFY(classAA); QCOMPARE(classAA->baseClassesSize(), 1u); QCOMPARE(classAA->baseClasses()[0].access, Declaration::Public); ClassDeclaration* classA = dynamic_cast(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(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(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(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(top->localDeclarations()[2])); QCOMPARE(static_cast(top->localDeclarations()[2])->declaration(), top->childContexts()[1]->localDeclarations()[0]); QCOMPARE(top->localDeclarations()[2], FunctionDefinition::definition(top->childContexts()[1]->localDeclarations()[0])); QVERIFY(dynamic_cast(top->localDeclarations()[3])); QCOMPARE(static_cast(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(top->localDeclarations()[4])); QCOMPARE(static_cast(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(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 void test(T t); B(int i); int test(int a); int test(char a); template void test(T2 t, T3 t3); int test(Unknown k); int test(Unknown2 k); }; template void B::test(T t) {}; B::B(int) {}; int B::test(int a){}; int B::test(char a){}; template 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 decls = top->findDeclarations(QualifiedIdentifier("Class")); QCOMPARE(decls.size(), 1); kDebug() << "qualified identifier:" << top->childContexts()[0]->localDeclarations()[0]->qualifiedIdentifier(); QVERIFY(!dynamic_cast(top->childContexts()[0]->localDeclarations()[0])); QVERIFY(dynamic_cast(top->childContexts()[0]->localDeclarations()[0])); QVERIFY(static_cast(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\n\ class ClassE\n\ {\n\ public:\n\ template\n\ void E_FuncA(E_A_T1)\n\ {\n\ }\n\ };\n\ template\n\ ClassE::~ClassE() = default;\n\ template<>\n\ template<>\n\ void ClassE::E_FuncA(ClassA);\n\ template<>\n\ template<>\n\ void ClassE::E_FuncA(ClassA)\n\ {\n\ }"); LockedTopDUContext top = parse(text, DumpNone); QCOMPARE(top->localDeclarations().count(), 5); QVERIFY(dynamic_cast(top->localDeclarations()[2])); QVERIFY(!dynamic_cast(top->localDeclarations()[3])); QVERIFY(dynamic_cast(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); QCOMPARE(top->childContexts()[0]->localDeclarations()[0]->uses().size(), 1); QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().size(), 1); } { // also see https://bugs.kde.org/show_bug.cgi?id=300347 const QByteArray text = "struct B { B(); }; void test() { B a; B b(); }"; LockedTopDUContext top = parse(text, DumpAll); QCOMPARE(top->childContexts().size(), 3); QCOMPARE(top->childContexts()[2]->localDeclarations().size(), 2); QCOMPARE(top->childContexts()[0]->localDeclarations().size(), 1); QCOMPARE(top->childContexts()[0]->localDeclarations()[0]->uses().begin()->size(), 2); // Grab first use of B(), 'B a' => range points to position after 'a', start == end RangeInRevision range = top->childContexts()[0]->localDeclarations()[0]->uses().begin().value()[0]; QCOMPARE(range, RangeInRevision(0, 36, 0, 36)); // Grab second use of B(), 'B b()' => range points to '(' range = top->childContexts()[0]->localDeclarations()[0]->uses().begin().value()[1]; QCOMPARE(range, RangeInRevision(0, 41, 0, 42)); } { // 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 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\n"; text += "class Q {\n"; text += "public:\n"; text += " Q(T var) { }\n"; text += " Q() { }\n"; text += "};\n"; text += "template\n"; text += "class B : public Q {\n"; text += "public:\n"; text += " B(T 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(), 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 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().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().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().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 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()); Declaration* defClassB = top->localDeclarations()[1]; QCOMPARE(defClassB->identifier(), Identifier("B")); QVERIFY(defClassB->type()); ClassDeclaration* defClassC = dynamic_cast(top->localDeclarations()[2]); QVERIFY(defClassC); QCOMPARE(defClassC->identifier(), Identifier("C")); QVERIFY(defClassC->type()); QCOMPARE( defClassC->baseClassesSize(), 1u ); ClassDeclaration* defClassD = dynamic_cast(top->localDeclarations()[3]); QVERIFY(defClassD); QCOMPARE(defClassD->identifier(), Identifier("D")); QVERIFY(defClassD->type()); 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(top->localDeclarations()[4]); QVERIFY(defClassF); QCOMPARE(defClassF->identifier(), Identifier("F")); QVERIFY(defClassF->type()); QCOMPARE( defClassF->baseClassesSize(), 1u ); QVERIFY( defClassF->baseClasses()[0].baseClass.type().unsafeData() ); Declaration* FDDecl = findDeclaration(top, QualifiedIdentifier("F") ); 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()); 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(); 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( 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("templateclass 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{}; templateclass AsPointer {typedef T* Ptr;}; templateclass AsPointer {typedef T* Ptr;}; templateclass AsPointer {typedef T* Ptr2;}; templateclass AsPointer {typedef T* Ptr2;};"); LockedTopDUContext top = parse(text, DumpNone); QCOMPARE(top->localDeclarations().count(), 5); TemplateDeclaration* base = dynamic_cast(top->localDeclarations()[1]); QVERIFY(base); QCOMPARE(base->specializationsSize(), 3u); TemplateDeclaration* sp3AsTemplate = dynamic_cast(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()); QVERIFY(! (sp3AsTemplate->specializedWith().information().templateParameters()[0].abstractType()->modifiers() & AbstractType::ConstModifier) ); QVERIFY(sp3AsTemplate->specializedWith().information().templateParameters()[0].type()->baseType()); QVERIFY(sp3AsTemplate->specializedWith().information().templateParameters()[0].type()->baseType().cast()); QVERIFY(sp3AsTemplate->specializedWith().information().templateParameters()[0].type()->baseType().cast()->identifier().isConstant()); Declaration* decl2 = findDeclaration(top, QualifiedIdentifier("AsPointer::Ptr2")); Declaration* decl1 = findDeclaration(top, QualifiedIdentifier("AsPointer::Ptr")); Declaration* decl3 = findDeclaration(top, QualifiedIdentifier("AsPointer::Ptr")); Declaration* decl4 = findDeclaration(top, QualifiedIdentifier("AsPointer::Ptr2")); QVERIFY(decl1); QVERIFY(decl2); QVERIFY(decl3); QVERIFY(decl4); QVERIFY(unAliasedType(decl1->abstractType()).cast()); 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 E{typedef A Type1;}; template class E { typedef B Type2; typedef T2 NotA; }; template class E { 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(EDecl); TemplateDeclaration* templateE1Decl = dynamic_cast(E1Decl); TemplateDeclaration* templateE2Decl = dynamic_cast(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()); kDebug() << typeid(*templateE1Decl->specializedWith().information().templateParameters()[1].abstractType()).name(); QVERIFY(templateE1Decl->specializedWith().information().templateParameters()[1].type()); QVERIFY(templateE2Decl->specializedWith().information().templateParameters()[0].type()); QVERIFY(!templateE2Decl->specializedWith().information().templateParameters()[1].type()); QCOMPARE(dynamic_cast(templateE1Decl->specializedFrom().data()), templateEDecl); QCOMPARE(dynamic_cast(templateE2Decl->specializedFrom().data()), templateEDecl); Declaration* foundE1Specialization = findDeclaration(top, QualifiedIdentifier("E::Type2")); QVERIFY(foundE1Specialization); Declaration* foundE1Specialization2 = findDeclaration(top, QualifiedIdentifier("E::NotA")); QVERIFY(foundE1Specialization2); QCOMPARE(unAliasedType(foundE1Specialization2->abstractType())->indexed(), top->localDeclarations()[2]->indexedType()); Declaration* foundE2Specialization = findDeclaration(top, QualifiedIdentifier("E::Type3")); QVERIFY(foundE2Specialization); Declaration* foundE2Specialization2 = findDeclaration(top, QualifiedIdentifier("E::NotA")); QVERIFY(foundE2Specialization2); QCOMPARE(unAliasedType(foundE2Specialization2->abstractType())->indexed(), top->localDeclarations()[2]->indexedType()); QVERIFY(findDeclaration(top, QualifiedIdentifier("E"))); QCOMPARE(findDeclaration(top, QualifiedIdentifier("E"))->identifier(), Identifier("E")); } { // Test template specializations of nested classes QByteArray text("struct A { struct B { template struct C {}; }; };\n" "template<> struct A::B::C { typedef int Type; };\n"); LockedTopDUContext top = parse(text, DumpNone); Declaration* specialization = findDeclaration(top, QualifiedIdentifier("A::B::C::Type")); QVERIFY(specialization); } { // 0 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890123456789 QByteArray text("template\n" "class A { void foo(T arg); };\n" "template void A::foo(T arg) {}"); LockedTopDUContext top = parse(text, DumpNone); QCOMPARE(top->localDeclarations().count(), 2); TemplateDeclaration* base = dynamic_cast(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 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(type.unsafeData()); if( integral ) return (int)integral->value(); else return 0; } void TestDUChain::testTemplateRecursiveInstantiation() { { QByteArray text("template class A { public: bool member; enum { SizeWithFalse = sizeof(A) }; };"); LockedTopDUContext top = parse(text, DumpNone); Declaration* aTrueDecl = findDeclaration(top, Identifier("A")); Declaration* aFalseDecl = findDeclaration(top, Identifier("A")); 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 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< true >")); } { QByteArray text("template 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< int(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< int(5) >")); } { QByteArray text("template 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< int(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< int(9) >")); } { QByteArray text("class A {enum { Val = 5}; }; class B { enum{ Val = 7 }; }; template 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(testDecl); QVERIFY(templateTestDecl); Declaration* tempDecl = findDeclaration( top, QualifiedIdentifier("Test::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::Num") ); QVERIFY(tempDecl); QCOMPARE(value(tempDecl->abstractType()), 3); tempDecl = findDeclaration( top, QualifiedIdentifier("Test::Sum") ); QVERIFY(tempDecl->abstractType()); QCOMPARE(value(tempDecl->abstractType()), 8); tempDecl = findDeclaration( top, QualifiedIdentifier("Test::TempVal") ); QVERIFY(tempDecl->abstractType()); QCOMPARE(value(tempDecl->abstractType()), 7); tempDecl = findDeclaration( top, QualifiedIdentifier("Test::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::Sum") ); QVERIFY(tempDecl->abstractType()); QCOMPARE(value(tempDecl->abstractType()), 16); } } void TestDUChain::testIntegralTemplates() { QByteArray text("template 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::i") ); QVERIFY(dec); AbstractType::Ptr t = dec->abstractType(); IntegralType* integral = dynamic_cast( t.unsafeData() ); QVERIFY( integral ); QCOMPARE( integral->dataType(), (uint)IntegralType::TypeInt ); QCOMPARE(top->childContexts()[1]->localDeclarations().count(), 1); dec = findDeclaration( top, QualifiedIdentifier( "A::i") ); t = dec->abstractType(); integral = dynamic_cast( 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::i") ); t = dec->abstractType(); integral = dynamic_cast( t.unsafeData() ); QVERIFY( integral ); QCOMPARE( integral->dataType(), (uint)IntegralType::TypeDouble ); QCOMPARE( integral->modifiers(), (unsigned long long)AbstractType::LongModifier ); } void TestDUChain::testFunctionTemplates() { QByteArray method("template 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()); QVERIFY( isTemplateDeclaration(defTest) ); QVERIFY(defTest->internalContext()); QCOMPARE(defTest->internalContext()->importedParentContexts().size(), 1); KDevelop::DUContext* tempCtx = getTemplateContext(defTest); QVERIFY(tempCtx); QCOMPARE( defTest->type()->arguments().count(), 1 ); QVERIFY( realType(defTest->type()->arguments()[0], 0).cast() ); } void TestDUChain::testTemplateFunctions() { QByteArray method("class A {}; template T a(T& q) {};template 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")); QVERIFY(d); FunctionType::Ptr cppFunction = d->type(); 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 > 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")); QVERIFY(instTC); QVERIFY(instTC->internalContext()); QList 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()); QCOMPARE(testDecl->internalContext()->localDeclarations(top).count(), 1); kDebug() << testDecl->abstractType()->toString(); QCOMPARE(testDecl->type()->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 B { class Q{ typedef T Type; }; }; B::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 class Factorial{ enum { Value = value * Factorial::Value };}; template<> class Factorial<0> { enum { Value = 1 };};"); LockedTopDUContext top = parse(method, DumpNone); QCOMPARE(top->localDeclarations().count(), 2); TemplateDeclaration* templateBase = dynamic_cast(top->localDeclarations()[0]); TemplateDeclaration* templateSpecialization = dynamic_cast(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()); QCOMPARE(templateSpecialization->specializedWith().information().templateParameters()[0].type()->value(), 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()); Declaration* factorial0 = findDeclaration(top, QualifiedIdentifier("Factorial<0>::Value")); QVERIFY(factorial0); QVERIFY(factorial0->type()); QCOMPARE(factorial0->type()->value(), 1); Declaration* factorial2 = findDeclaration(top, QualifiedIdentifier("Factorial<2>::Value")); QVERIFY(factorial2); QVERIFY(factorial2->type()); QCOMPARE(factorial2->type()->value(), 2); Declaration* factorial3 = findDeclaration(top, QualifiedIdentifier("Factorial<3>::Value")); QVERIFY(factorial3); QVERIFY(factorial3->type()); QCOMPARE(factorial3->type()->value(), 6); Declaration* factorial4 = findDeclaration(top, QualifiedIdentifier("Factorial<4>::Value")); QVERIFY(factorial4); QVERIFY(factorial4->type()); QCOMPARE(factorial4->type()->value(), 24); } void TestDUChain::testMetaProgramming3() { QByteArray method("templateclass 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()); QCOMPARE(decl->type()->value(), 2); kDebug() << "test 2"; decl = findDeclaration(top, QualifiedIdentifier("bigger_than<5, 5>::Result")); QVERIFY(decl); QVERIFY(decl->type()); QCOMPARE(decl->type()->value(), 0); decl = findDeclaration(top, QualifiedIdentifier("bigger_than<5, 6>::Result")); QVERIFY(decl); QVERIFY(decl->type()); QCOMPARE(decl->type()->value(), -2); } void TestDUChain::testMetaProgramming2() { QByteArray method("templateclass Permutations {public:enum { value = N*(Permutations::value) };};templateclass Permutations {public:enum { value = 1 };};"); LockedTopDUContext top = parse(method, DumpNone); QCOMPARE(top->localDeclarations().count(), 2); TemplateDeclaration* templateBase = dynamic_cast(top->localDeclarations()[0]); TemplateDeclaration* templateSpecialization = dynamic_cast(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()); QCOMPARE(templateSpecialization->specializedWith().information().templateParameters()[1].type()->value(), 0); Declaration* permutations0 = findDeclaration(top, QualifiedIdentifier("Permutations<5, 0>::value")); QVERIFY(permutations0); QVERIFY(permutations0->type()); QCOMPARE(permutations0->type()->value(), 1); Declaration* permutations2 = findDeclaration(top, QualifiedIdentifier("Permutations<2, 1>::value")); QVERIFY(permutations2); QVERIFY(permutations2->type()); QCOMPARE(permutations2->type()->value(), 2); Declaration* permutations3 = findDeclaration(top, QualifiedIdentifier("Permutations<4, 2>::value")); QVERIFY(permutations3); QVERIFY(permutations3->type()); QCOMPARE(permutations3->type()->value(), 12); Declaration* permutations4 = findDeclaration(top, QualifiedIdentifier("Permutations<10, 5>::value")); QVERIFY(permutations4); QVERIFY(permutations4->type()); kDebug() << permutations4->abstractType()->toString(); QCOMPARE(permutations4->type()->value(), 30240); } void TestDUChain::testTemplateInternalSearch() { QByteArray method("class A {}; template 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()); QVERIFY(top->childContexts()[2]->localDeclarations()[0]->type()); QVERIFY(top->childContexts()[2]->localDeclarations()[0]->type()->returnType().cast()); Declaration* d = findDeclaration(top, QualifiedIdentifier("B::mem")); QVERIFY(d); kDebug() << d->toString(); FunctionType::Ptr fType = d->type(); QVERIFY(fType); QVERIFY(fType->returnType()); kDebug() << fType->toString(); QCOMPARE(fType->returnType()->toString(), QString("B< A >")); d = findDeclaration(top, QualifiedIdentifier("B::mem2")); QVERIFY(d); QCOMPARE(d->abstractType()->toString(), QString("const B< A >")); } void TestDUChain::testTemplateImplicitInstantiations() { { QByteArray method("class A { template 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(top->childContexts().first()->localDeclarations().first()); QVERIFY(tpl); QCOMPARE(tpl->instantiations().size(), 3); } { QByteArray method("template 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(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(), 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 decls = top->findDeclarations(QualifiedIdentifier("A::B::foo")); QCOMPARE(decls.size(), 1); FunctionDeclaration* fooDecl = dynamic_cast(decls.first()); QVERIFY(fooDecl); FunctionType::Ptr fooType = fooDecl->type(); 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()); QVERIFY(refType.cast()->baseType()); QVERIFY(refType.cast()->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 decls = top->findDeclarations(QualifiedIdentifier("A::B::C::foo")); QCOMPARE(decls.size(), 1); FunctionDeclaration* fooDecl = dynamic_cast(decls.first()); QVERIFY(fooDecl); FunctionType::Ptr fooType = fooDecl->type(); 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()); QVERIFY(top->localDeclarations()[1]->abstractType().cast()); QVERIFY(top->localDeclarations()[2]->abstractType().cast()); QCOMPARE(top->localDeclarations()[0]->abstractType().cast()->qualifiedIdentifier(), QualifiedIdentifier("honk")); QCOMPARE(top->localDeclarations()[0]->abstractType().cast()->type()->toString(), QString("int*")); QCOMPARE(top->localDeclarations()[1]->abstractType().cast()->qualifiedIdentifier(), QualifiedIdentifier("honk2")); ///@todo Make this work as well, the init-declarators need to have separate types // QCOMPARE(top->localDeclarations()[1]->abstractType().cast()->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()); QCOMPARE(Cpp::simplifiedTypeString(top->localDeclarations()[0]->abstractType().cast()->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 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*>::Member").remove(' ')); } { ///@todo Add more tests for this QByteArray method("template struct F; template class Tc; Tc*> t; const Tc*>**const*& Test1; Tc*>* 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*>").remove(' ')); QCOMPARE(Cpp::simplifiedTypeString(top->localDeclarations()[3]->abstractType(), top).remove(' '), QString("constTc*>**const*&").remove(' ')); FunctionType::Ptr funType = top->localDeclarations()[4]->abstractType().cast(); 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(), 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(); 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 CC; void test(CC& item); const A& a;const A*** b;CC 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()); QCOMPARE(argType->toString().remove(' '), QString("CC&")); { // QWidget* navigationWidget = top->childContexts()[1]->createNavigationWidget(top->childContexts()[1]->localDeclarations()[0]); // QVERIFY(navigationWidget); // KDevelop::AbstractNavigationWidget* nWidget = dynamic_cast(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&")); 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")); QCOMPARE(Cpp::simplifiedTypeString(top->localDeclarations()[5]->abstractType(), top).remove(' '), QString("CC")); } typedef QList > ClassInstantiations; struct DeclarationTestData { DeclarationTestData() : useCount(0), specializedFrom(0) {}; //Declaration int useCount; //TemplateDeclaration ClassInstantiations instantiations; QList instantiationsTestData; Declaration* specializedFrom; QList specializations; }; InstantiationInformation instantiationInfoForDeclarations(const QList &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(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(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\n\ class ClassE\n\ {\n\ public:\n\ template\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::E_FuncA(ClassA, ClassA);\n\ template<>\n\ template<>\n\ void ClassE::E_FuncA(ClassA, ClassA)\n\ {\n\ }\n\ template<>\n\ void ClassE::E_FuncB(ClassA, ClassB);\n\ template<>\n\ void ClassE::E_FuncB(ClassA, ClassB)\n\ {\n\ }\n\ int main()\n\ {\n\ ClassE foo;\n\ foo.E_FuncA(ClassA(), ClassA());\n\ foo.E_FuncB(ClassA(), ClassB());\n\ ClassE 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(ClassA)); Declaration *ClassB = top->localDeclarations()[currentTopDecl++]; Q_ASSERT(dynamic_cast(ClassB)); Declaration *ClassC = top->localDeclarations()[currentTopDecl++]; Q_ASSERT(dynamic_cast(ClassC)); Declaration *ClassD = top->localDeclarations()[currentTopDecl++]; Q_ASSERT(dynamic_cast(ClassD)); Declaration *ClassE = top->localDeclarations()[currentTopDecl++]; Q_ASSERT(dynamic_cast(ClassE)); Q_ASSERT(dynamic_cast(ClassE)); //Declarations in ClassE context int currentClassEDecl = 0; Declaration *E_FuncA = ClassE->internalContext()->localDeclarations()[currentClassEDecl++]; Q_ASSERT(dynamic_cast(E_FuncA)); Q_ASSERT(dynamic_cast(E_FuncA)); Declaration *E_FuncB = ClassE->internalContext()->localDeclarations()[currentClassEDecl++]; Q_ASSERT(dynamic_cast(E_FuncB)); Q_ASSERT(dynamic_cast(E_FuncB)); Declaration *E_A_B_FuncA_A_A_Decl = top->localDeclarations()[currentTopDecl++]; Q_ASSERT(dynamic_cast(E_A_B_FuncA_A_A_Decl)); Q_ASSERT(dynamic_cast(E_A_B_FuncA_A_A_Decl)); Declaration *E_A_B_FuncA_A_A = top->localDeclarations()[currentTopDecl++]; Q_ASSERT(dynamic_cast(E_A_B_FuncA_A_A)); Q_ASSERT(dynamic_cast(E_A_B_FuncA_A_A)); Q_ASSERT(dynamic_cast(E_A_B_FuncA_A_A)); Declaration *E_A_B_FuncBDecl = top->localDeclarations()[currentTopDecl++]; Q_ASSERT(dynamic_cast(E_A_B_FuncBDecl)); Declaration *E_A_B_FuncB = top->localDeclarations()[currentTopDecl++]; Q_ASSERT(dynamic_cast(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() << ClassA << ClassB) << (QList() << ClassC << ClassD); ClassE_testData.instantiationsTestData << ClassE_A_B_testData << ClassE_C_D_testData; QVERIFY(validDeclaration(ClassE, ClassE_testData)); //Test for ClassE::E_FuncA 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() << ClassA << ClassB << 0 << ClassA << ClassA) << (QList() << ClassC << ClassD << 0 << ClassA << ClassA) //These two instantiations shouldn't exist technically << (QList() << ClassA << ClassB << 0) << (QList() << 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::E_FuncA 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() << ClassA << ClassB << 0) << (QList() << 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::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 T test(const T& t) {}; template class A {T2 a; typedef T Template1; }; class B{int b;}; class C{int c;}; templateclass A{}; typedef A D;"); LockedTopDUContext top = parse(method, DumpNone); Declaration* defTest = top->localDeclarations()[0]; QCOMPARE(defTest->identifier(), Identifier("test")); QVERIFY(defTest->type()); QVERIFY( isTemplateDeclaration(defTest) ); Declaration* defClassA = top->localDeclarations()[1]; QCOMPARE(defClassA->identifier(), Identifier("A")); QVERIFY(defClassA->type()); QVERIFY( isTemplateDeclaration(defClassA) ); Declaration* defClassB = top->localDeclarations()[2]; QCOMPARE(defClassB->identifier(), Identifier("B")); QVERIFY(defClassB->type()); QVERIFY( !isTemplateDeclaration(defClassB) ); Declaration* defClassC = top->localDeclarations()[3]; QCOMPARE(defClassC->identifier(), Identifier("C")); QVERIFY(defClassC->type()); 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(); QVERIFY(delayed); QCOMPARE(delayed->identifier(), IndexedTypeIdentifier("T")); } ///Test creating a template instance of class A { 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(instanceDefClassA); QVERIFY(templateDecl); QVERIFY(instanceDefClassA != defClassA); QVERIFY(instanceDefClassA->context() == defClassA->context()); QVERIFY(instanceDefClassA->internalContext() != defClassA->internalContext()); QCOMPARE(instanceDefClassA->identifier(), Identifier("A")); QCOMPARE(instanceDefClassA->identifier().toString(), QString("A< B, C >")); QVERIFY(instanceDefClassA->abstractType()); AbstractType::Ptr t = instanceDefClassA->abstractType(); IdentifiedType* identifiedType = dynamic_cast(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").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 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()); Declaration* param2 = top->childContexts()[0]->localDeclarations()[1]; QVERIFY(param2->type()); TemplateParameterDeclaration* param2Decl = dynamic_cast(param2); QVERIFY(param2Decl); QVERIFY(!param2Decl->defaultParameter().isEmpty()); Declaration* param3 = top->childContexts()[0]->localDeclarations()[2]; QVERIFY(param3->type()); TemplateParameterDeclaration* param3Decl = dynamic_cast(param3); QVERIFY(param3Decl); QVERIFY(!param3Decl->defaultParameter().isEmpty()); } void TestDUChain::testTemplateDefaultParameters() { QByteArray method("struct S {} ; namespace std { template class Template1 { }; } template > class Template2 { typedef TT2 T1; };"); LockedTopDUContext top = parse(method, DumpNone); kDebug() << "searching"; // Declaration* memberDecl = findDeclaration(top, QualifiedIdentifier("Template2::T1")); Declaration* memberDecl = findDeclaration(top, QualifiedIdentifier("Template2::TT2")); QVERIFY(memberDecl); QVERIFY(memberDecl->abstractType()); QVERIFY(!memberDecl->type()); QCOMPARE(memberDecl->abstractType()->toString(), QString("std::Template1< S >")); } void TestDUChain::testTemplates3() { QByteArray method("typedef int quakka; template 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::cv")); QVERIFY(cvDecl); QVERIFY(cvDecl->abstractType()); AbstractType::Ptr type = cvDecl->abstractType(); IdentifiedType* idType = dynamic_cast(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::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::ConstValueRef")); QVERIFY(cvrDecl); AbstractType::Ptr type = cvrDecl->abstractType(); IdentifiedType* idType = dynamic_cast(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(); 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::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(); 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 struct Cnt { typedef T Val; }; struct Item; template struct Freqto { struct Item { typedef Value Value2; }; struct Pattern : public Cnt { }; };"); 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(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()); kDebug() << classDecl->baseClasses()[0].baseClass.abstractType()->toString(); Declaration* val2Decl = findDeclaration(top, QualifiedIdentifier("Freqto::Pattern::Val::Value2")); QVERIFY(val2Decl); QCOMPARE(unAliasedType(val2Decl->abstractType())->toString(), QString("int")); } } void TestDUChain::testTemplates4() { { QByteArray method("template 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::Mem"));; QVERIFY(memDecl); QCOMPARE(unAliasedType(memDecl->abstractType())->toString(), QString("B::A")); QVERIFY(findDeclaration(top->childContexts()[2], QualifiedIdentifier("Temp::Mem::AMember"))); } { QByteArray method("template struct Cnt { typedef T Val; }; struct Item; template struct Freqto { struct Item { typedef Value Value2; }; struct Pattern : public Cnt { }; };"); 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(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()); kDebug() << classDecl->baseClasses()[0].baseClass.abstractType()->toString(); Declaration* val2Decl = findDeclaration(top, QualifiedIdentifier("Freqto::Pattern::Val::Value2")); QVERIFY(val2Decl); QCOMPARE(unAliasedType(val2Decl->abstractType())->toString(), QString("int")); } } void TestDUChain::testTemplates2() { QByteArray method("struct S {} ; template class Base { struct Alloc { typedef TT& referenceType; }; }; template struct Class : public Base { typedef typename Base::Alloc Alloc; typedef typename Alloc::referenceType reference; reference member; }; Class 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()); QVERIFY(top->childContexts().count() == 5); QVERIFY(top->childContexts()[4]->localDeclarations().count() > 1); QVERIFY(top->childContexts()[4]->localDeclarations()[0]->type()); QVERIFY(top->childContexts()[4]->localDeclarations()[1]->type()); QVERIFY(top->childContexts()[4]->localDeclarations()[0]->type()->type().cast()); QVERIFY(top->childContexts()[4]->localDeclarations()[1]->type()->type().cast()); QVERIFY(top->childContexts()[4]->localDeclarations()[2]->type()); Declaration* memberDecl; kDebug() << "checking member"; memberDecl = findDeclaration(top, QualifiedIdentifier("Class::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::Alloc::referenceType")); // QVERIFY(memberDecl); // QVERIFY(memberDecl->abstractType()); // QCOMPARE(memberDecl->abstractType()->toString(), QString("S&"/*)); memberDecl = findDeclaration(top, QualifiedIdentifier("Class::member")); QVERIFY(memberDecl); QVERIFY(unAliasedType(memberDecl->abstractType())); QCOMPARE(unAliasedType(memberDecl->abstractType())->toString(), QString("S*&")); } void TestDUChain::testSpecializationSelection() { QByteArray method("template struct Foo { void normal() {} };\n" "template struct Foo { void typeIsConst() {} };\n" "template struct Foo { void typeIsVolatile() {} };\n" "template struct Foo { void typeIsConstVolatile() {} };\n" "template struct Foo { void typeIsPtr() {} };\n" "template struct Foo { void typeIsPtrPtr() {} };\n" "template struct Foo { void typeIsConstPtr() {} };\n" "template struct Foo { void typeIsConstPtrPtr() {} };\n" "template struct Foo { void typeIsRValue() {} };\n" "template struct Foo { void typeIsConstRValue() {} };\n" "template struct Foo { void typeIsArray() {} };\n" "template struct Foo { void typeIsConstArray() {} };\n" "template struct Foo { void typeIsConstPtrRValue() {} };\n" "template struct Foo { void typeIsConstPtrConstRValue() {} };\n"); LockedTopDUContext top = parse(method, DumpNone); QVERIFY(findDeclaration(top, QualifiedIdentifier("Foo::normal"))); QVERIFY(findDeclaration(top, QualifiedIdentifier("Foo::typeIsConst"))); QVERIFY(findDeclaration(top, QualifiedIdentifier("Foo::typeIsVolatile"))); QVERIFY(findDeclaration(top, QualifiedIdentifier("Foo::typeIsConstVolatile"))); QVERIFY(findDeclaration(top, QualifiedIdentifier("Foo::typeIsPtr"))); QVERIFY(findDeclaration(top, QualifiedIdentifier("Foo::typeIsConstPtr"))); QVERIFY(findDeclaration(top, QualifiedIdentifier("Foo::typeIsRValue"))); QVERIFY(findDeclaration(top, QualifiedIdentifier("Foo::typeIsConstRValue"))); QVERIFY(findDeclaration(top, QualifiedIdentifier("Foo::typeIsPtrPtr"))); QVERIFY(findDeclaration(top, QualifiedIdentifier("Foo::typeIsConstPtrPtr"))); QVERIFY(findDeclaration(top, QualifiedIdentifier("Foo::typeIsArray"))); QVERIFY(findDeclaration(top, QualifiedIdentifier("Foo::typeIsConstArray"))); QVERIFY(findDeclaration(top, QualifiedIdentifier("Foo::typeIsConstPtrRValue"))); QVERIFY(findDeclaration(top, QualifiedIdentifier("Foo::typeIsConstPtrConstRValue"))); QVERIFY(findDeclaration(top, QualifiedIdentifier("Foo>::normal"))); } void TestDUChain::testSpecializationSelection2() { QByteArray method("template struct Simple {};\n" "template struct Simple {};\n" "template struct Foo { void normal() {} };\n" "template struct Foo { void normal2() {} };\n" "template struct Foo { void normal3() {} };\n" "template struct Foo, int> { void normal4() {} };\n" "template struct Foo> { void normal5() {} };\n"); LockedTopDUContext top = parse(method, DumpNone); QVERIFY(findDeclaration(top, QualifiedIdentifier("Foo::normal"))); QVERIFY(findDeclaration(top, QualifiedIdentifier("Foo::normal2"))); QVERIFY(findDeclaration(top, QualifiedIdentifier("Foo::normal3"))); QVERIFY(findDeclaration(top, QualifiedIdentifier("Foo, int>::normal4"))); QVERIFY(findDeclaration(top, QualifiedIdentifier("Foo>::normal5"))); } void TestDUChain::testTemplatesRebind() { QByteArray method("struct A {}; struct S {typedef A Value;} ; template class Base { template struct rebind { typedef Base other; }; typedef TT Type; }; template class Class { typedef Base::rebind::other::Type MemberType; MemberType member; Base::template rebind::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"))); QVERIFY(!findDeclaration(top, QualifiedIdentifier("Base"))->type()); QVERIFY(findDeclaration(top, QualifiedIdentifier("Base::rebind"))); QVERIFY(!findDeclaration(top, QualifiedIdentifier("Base::rebind::other"))->type()); QVERIFY(findDeclaration(top, QualifiedIdentifier("Base::rebind::other::Type"))); QVERIFY(!findDeclaration(top, QualifiedIdentifier("Base::rebind::other::Type"))->type()); Declaration* memberDecl = findDeclaration(top, QualifiedIdentifier("Base::rebind::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::member")); QVERIFY(memberDecl); QVERIFY(memberDecl->abstractType()); QVERIFY(unAliasedType(memberDecl->abstractType())); QCOMPARE(unAliasedType(memberDecl->abstractType())->toString(), QString("S")); Declaration* member3Decl = findDeclaration(top, QualifiedIdentifier("Class::value")); QVERIFY(member3Decl); QVERIFY(member3Decl->abstractType()); QVERIFY(unAliasedType(member3Decl->abstractType())); QCOMPARE(unAliasedType(member3Decl->abstractType())->toString(), QString("A")); Declaration* member2Decl = findDeclaration(top, QualifiedIdentifier("Class::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 Test { Test(); }; template class Class { typedef typename T::Value Value; typename T::Value value; typedef Test ValueClass; Test ValueClass2;};"); LockedTopDUContext top = parse(method, DumpNone); QList constructors = TypeUtils::getConstructors( top->localDeclarations()[2]->abstractType().cast(), 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::Value")); QVERIFY(valueAliasDecl); TypeAliasType::Ptr alias = valueAliasDecl->type(); QVERIFY(alias); QVERIFY(alias->type()); QVERIFY(!alias->type().cast()); QCOMPARE(unAliasedType(alias.cast())->toString(), QString("A")); kDebug() << "aliased type:" << alias->type()->toString(); kDebug() << "typedef type:" << alias->toString(); kDebug() << "un-aliased type:" << unAliasedType(alias.cast())->toString(); Declaration* member5Decl = findDeclaration(top, QualifiedIdentifier("Class::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::ValueClass")); QVERIFY(member4Decl); QVERIFY(unAliasedType(member4Decl->abstractType())); QCOMPARE(unAliasedType(member4Decl->abstractType())->toString(), QString("Test< S::Value >")); Declaration* member3Decl = findDeclaration(top, QualifiedIdentifier("Class::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(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(top->localDeclarations()[0])); QVERIFY(!dynamic_cast(top->localDeclarations()[2])); CppClassType::Ptr type1 = top->localDeclarations()[0]->type(); kDebug() << typeid(*top->localDeclarations()[1]->abstractType()).name(); CppClassType::Ptr type2 = top->localDeclarations()[1]->type(); CppClassType::Ptr type3 = top->localDeclarations()[2]->type(); CppClassType::Ptr type4 = top->localDeclarations()[3]->type(); CppClassType::Ptr type5 = top->childContexts()[1]->localDeclarations()[0]->type(); 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(); 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 struct Fruk { Fruk& operator[](int); }; Fruk 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(top->localDeclarations()[0])); CppClassType::Ptr type1 = top->localDeclarations()[0]->type(); CppClassType::Ptr type2 = top->localDeclarations()[1]->type(); CppClassType::Ptr type3 = top->localDeclarations()[2]->type(); CppClassType::Ptr type4 = top->localDeclarations()[4]->type(); CppClassType::Ptr type5 = top->childContexts()[1]->localDeclarations()[0]->type(); CppClassType::Ptr type12 = top->localDeclarations()[3]->type(); 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(); 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(top->childContexts()[0]->localDeclarations()[0])); ForwardDeclaration* forwardDecl = static_cast(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::Ptr type2 = top->localDeclarations()[1]->type(); CppClassType::Ptr type3 = top->childContexts()[1]->localDeclarations()[0]->type(); CppClassType::Ptr type4 = top->localDeclarations()[3]->type(); CppClassType::Ptr type5 = top->childContexts()[2]->childContexts()[0]->localDeclarations()[0]->type(); 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(); 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(top->localDeclarations()[0])); ForwardDeclaration* forwardDecl = static_cast(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{}; templateclass Test; Test t; templateclass Test {}; "); LockedTopDUContext top = parse(method, DumpNone); QCOMPARE(top->localDeclarations().count(), 4); QVERIFY(top->localDeclarations()[1]->internalContext()); QVERIFY(dynamic_cast(top->localDeclarations()[1])); CppClassType::Ptr type1 = top->localDeclarations()[1]->type(); CppClassType::Ptr type2 = top->localDeclarations()[2]->type(); CppClassType::Ptr type3 = top->localDeclarations()[3]->type(); QVERIFY(type1); QVERIFY(type2); QVERIFY(type3); TemplateDeclaration* temp1Decl = dynamic_cast(type1->declaration(top)); QVERIFY(temp1Decl); TemplateDeclaration* temp2Decl = dynamic_cast(type2->declaration(top)); QVERIFY(temp2Decl); TemplateDeclaration* temp3Decl = dynamic_cast(type3->declaration(top)); QVERIFY(temp3Decl); TemplateDeclaration* temp1DeclResolved = dynamic_cast(type1->declaration(top)); QVERIFY(temp1DeclResolved); TemplateDeclaration* temp2DeclResolved = dynamic_cast(type2->declaration(top)); QVERIFY(temp2DeclResolved); QVERIFY(dynamic_cast(temp1DeclResolved)->type()); QVERIFY(dynamic_cast(temp2DeclResolved)->type()); QCOMPARE(temp2Decl->instantiatedFrom(), temp1Decl); QCOMPARE(temp2DeclResolved->instantiatedFrom(), temp1DeclResolved); QVERIFY(type2->declaration(0)->abstractType().unsafeData()); QVERIFY(type2->declaration(0)->type().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{}; templateclass Test; Test t; templateclass 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(top->localDeclarations()[1])); CppClassType::Ptr type1 = top->localDeclarations()[1]->type(); CppClassType::Ptr type2 = top->localDeclarations()[2]->type(); CppClassType::Ptr type3 = top->localDeclarations()[3]->type(); QVERIFY(type1); QVERIFY(type2); QVERIFY(type3); QCOMPARE(type1->declaration(0)->abstractType()->indexed(), type3->indexed()); TemplateDeclaration* temp1Decl = dynamic_cast(type1->declaration(top)); QVERIFY(temp1Decl); TemplateDeclaration* temp2Decl = dynamic_cast(type2->declaration(top)); QVERIFY(temp2Decl); Declaration* temp2DeclForward = dynamic_cast(type2->declaration(top)); QVERIFY(temp2DeclForward); TemplateDeclaration* temp3Decl = dynamic_cast(type3->declaration(top)); QVERIFY(temp3Decl); QCOMPARE(temp2Decl->instantiatedFrom(), temp1Decl); QCOMPARE(dynamic_cast(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 \n" "class Argument : public GenericArgument\n" "{\n" " public:\n" " Argument( const char* name, const T &value )\n" " : GenericArgument( name, static_cast( &value ) )\n" " {}\n" "};\n" "\n" "class A\n" "{\n" " public:\n" " A()\n" " {\n" " A::invokeMethod( this, \"hello\", Argument( \"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 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 > encountered; QMap< QPair, 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 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(); QVERIFY(a); QVERIFY(!(a->modifiers() & AbstractType::ConstModifier)); IntegralType::Ptr a2 = a->baseType().cast(); QVERIFY(a2); QVERIFY(a2->modifiers() & AbstractType::ConstModifier); ReferenceType::Ptr b = top->localDeclarations()[2]->type(); QVERIFY(b); QVERIFY(!(b->modifiers() & AbstractType::ConstModifier)); IntegralType::Ptr b2 = b->baseType().cast(); QVERIFY(b2); QVERIFY(b2->modifiers() & AbstractType::ConstModifier); ReferenceType::Ptr c = top->localDeclarations()[3]->type(); QVERIFY(c); QVERIFY(!(c->modifiers() & AbstractType::ConstModifier)); PointerType::Ptr c2 = c->baseType().cast(); QVERIFY(c2); QVERIFY(c2->modifiers() & AbstractType::ConstModifier); IntegralType::Ptr c3 = c2->baseType().cast(); QVERIFY(c3); QVERIFY(!(c3->modifiers() & AbstractType::ConstModifier)); PointerType::Ptr d = top->localDeclarations()[4]->type(); QVERIFY(d); QVERIFY(d->modifiers() & AbstractType::ConstModifier); IntegralType::Ptr d2 = d->baseType().cast(); QVERIFY(d2); QVERIFY(!(d2->modifiers() & AbstractType::ConstModifier)); CppClassType::Ptr e = top->localDeclarations()[5]->type(); QVERIFY(e); QVERIFY(e->modifiers() & AbstractType::ConstModifier); PointerType::Ptr f = top->localDeclarations()[6]->type(); QVERIFY(f); QVERIFY(!(f->modifiers() & AbstractType::ConstModifier)); CppClassType::Ptr f2 = f->baseType().cast(); QVERIFY(f2); QVERIFY(f2->modifiers() & AbstractType::ConstModifier); PointerType::Ptr g = top->localDeclarations()[7]->type(); QVERIFY(g); QVERIFY(g->modifiers() & AbstractType::ConstModifier); CppClassType::Ptr g2 = g->baseType().cast(); QVERIFY(g2); QVERIFY(!(g2->modifiers() & AbstractType::ConstModifier)); } { QByteArray method("class A; template class B; B ca;B 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(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()); QVERIFY(top->localDeclarations()[1]->type()->baseType()); QVERIFY(top->localDeclarations()[1]->type()->baseType()->modifiers() & AbstractType::ConstModifier); kDebug() << top->localDeclarations()[1]->type()->toString(); }} void TestDUChain::testDeclarationId() { QByteArray method("template class C { template 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::C2")); 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("code"); QTest::addColumn("datatype"); QTest::addColumn("constness"); QTest::addColumn("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(); 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 // " 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(); QVERIFY(type); StructureType::Ptr structType = type->baseType().cast(); QVERIFY(structType); QCOMPARE(structType->qualifiedIdentifier().toString(), QString("Foo")); } { Declaration* dec = ctx->findDeclarations(Identifier("v2")).first(); StructureType::Ptr type = dec->abstractType().cast(); 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(); QVERIFY(type); IntegralType::Ptr intType = type->baseType().cast(); 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(); QVERIFY(type); StructureType::Ptr structType = type->classType().cast(); 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(); { QVERIFY(type); StructureType::Ptr structType = type->classType().cast(); QVERIFY(structType); QCOMPARE(structType->qualifiedIdentifier().toString(), QString("BB")); } PtrToMemberType::Ptr ptype = type->baseType().cast(); { QVERIFY(ptype); StructureType::Ptr structType = ptype->classType().cast(); QVERIFY(structType); QCOMPARE(structType->qualifiedIdentifier().toString(), QString("AA")); } } } void TestDUChain::testMemberPtrCrash() { QByteArray code( "template\n" "class A{};\n" "class B{};\n" "class C{};\n" "A 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 class A;\n" "template struct B;\n" "template \n" "struct B : B, X> {};\n" // crucial: same typename like class above above "template struct B {};\n" ); LockedTopDUContext top = parse(code, DumpNone); QVERIFY(top); QVERIFY(top->problems().isEmpty()); Declaration *structB = top->localDeclarations()[3]; TemplateDeclaration *structBTemplate = dynamic_cast(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(structBSpec); QVERIFY(structBSpecTemplate); QVERIFY(structBSpecTemplate->instantiations().size() == 0); QVERIFY(structBSpecTemplate->specializationsSize() == 0); Declaration *structBSpec2 = top->localDeclarations()[5]; TemplateDeclaration *structBSpecTemplate2 = dynamic_cast(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 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 "moc_test_duchain.cpp"