/* This file is part of KDevelop Copyright 2009 Ramon Zarazua Copyright 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_cppcodegen.h" #include "parsesession.h" #include "ast.h" #include "astutilities.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "codegen/simplerefactoring.h" QTEST_KDEMAIN(TestCppCodegen, NoGUI ) using namespace KDevelop; ForegroundLock* globalTestLock = 0; void TestCppCodegen::initTestCase() { //Initialize KDevelop components AutoTestShell::init(QStringList() << "kdevcppsupport"); TestCore::initialize(Core::NoUi); Core::self()->languageController()->backgroundParser()->setDelay(1); DUChain::self()->disablePersistentStorage(); Core::self()->sourceFormatterController()->disableSourceFormatting(true); CodeRepresentation::setDiskChangesForbidden(true); globalTestLock = new ForegroundLock; } void TestCppCodegen::cleanupTestCase() { TestCore::shutdown(); delete globalTestLock; globalTestLock = 0; } void dumpAST(InsertIntoDUChain& code) { DUChainReadLocker lock; Q_ASSERT(!code->parsingEnvironmentFile()->isProxyContext()); ParseSession::Ptr session = ParseSession::Ptr::dynamicCast(code->ast()); Q_ASSERT(session); Cpp::DumpChain dump; dump.dump(session->topAstNode(), session.data()); } void dumpDUChain(InsertIntoDUChain& code) { DUChainReadLocker lock; KDevelop::dumpDUContext(code.topContext()); } void TestCppCodegen::testAssistants() { QFETCH(QString, contents); QFETCH(int, numAssistants); QFETCH(int, executeAssistant); QFETCH(QString, insertionText); InsertIntoDUChain code("test_assistants.cpp", contents); code.parse(TopDUContext::AllDeclarationsContextsUsesAndAST); DUChainReadLocker lock; QCOMPARE(code->problems().size(), 1); QVERIFY(code->problems().first()->solutionAssistant()); QCOMPARE(code->problems().first()->solutionAssistant()->actions().size(), numAssistants); code->problems().first()->solutionAssistant()->actions()[executeAssistant]->execute(); //Make sure the assistant has inserted the correct solution QVERIFY(code.m_insertedCode.text().contains(insertionText)); } void TestCppCodegen::testAssistants_data() { QTest::addColumn("contents"); QTest::addColumn("numAssistants"); QTest::addColumn("executeAssistant"); QTest::addColumn("insertionText"); /// NOTE: THIS CODE MUST _NOT_ DEPEND ON A SPECIFIC FORMATTER CONFIGURATION! QTest::newRow("local") << "enum Honk { Hank };\n" "void test() {\n" " val = Hank;\n" " }\n" << 1 << 0 << "Honk val = Hank;"; QString inClass = "enum Honk { Hank };\n" "class myClass {\n" "public:\n" " void test() {\n" " val = Hank;\n" " }\n" "};\n"; QTest::newRow("local_class") << inClass << 4 << 0 << "Honk val = Hank;"; QTest::newRow("private_class") << inClass << 4 << 1 << "private:\n Honk val;"; QTest::newRow("protected_class") << inClass << 4 << 2 << "protected:\n Honk val;"; QTest::newRow("public_class") << inClass << 4 << 3 << "}\n Honk val;"; QString inOtherClass = "class other {\n" "};\n" "class myClass : public other {\n" "public:\n" " void test() {\n" " other* o;\n" " o->foo(1, 0.5);\n" " }\n" "};\n"; QTest::newRow("public_other") << inOtherClass << 2 << 1 << "class other {\npublic:\nvoid foo(int arg1, double arg2);"; QTest::newRow("protected_other") << inOtherClass << 2 << 0 << "class other {\nprotected:\nvoid foo(int arg1, double arg2);"; } void TestCppCodegen::testUpdateIndices() { /// @todo Extend this test to make sure t hat all kinds of declarations retain their indices when they are updated { InsertIntoDUChain code1("duchaintest_1.h", "class QW{}; struct A { struct Member2; struct Member1; }; class Oq{};"); InsertIntoDUChain code3("duchaintest_3.h", "#include \n struct C : public A { Member1 m1; Member2 m2; A test(int arg) { int v1; \n{}\n { int v2, *v3; }} int test(); };"); kWarning() << "********************* Parsing step 1"; code3.parse(TopDUContext::AllDeclarationsContextsUsesAndAST); DUChainReadLocker lock; IndexedDeclaration CDecl = code3.getDeclaration("C"); QVERIFY(CDecl.isValid()); IndexedDeclaration ADecl = code3.getDeclaration("A"); QVERIFY(ADecl.isValid()); IndexedDeclaration C_m1 = code3.getDeclaration("C::m1"); QVERIFY(C_m1.isValid()); IndexedDeclaration C_m2 = code3.getDeclaration("C::m2"); QVERIFY(C_m2.isValid()); QVERIFY(CDecl.declaration()->internalContext()); QCOMPARE(CDecl.declaration()->internalContext()->localDeclarations().size(), 4); IndexedDeclaration C_test = CDecl.declaration()->internalContext()->localDeclarations()[2]; QVERIFY(C_test.isValid()); DUContext* testCtx = C_test.data()->internalContext(); QVERIFY(testCtx); QCOMPARE(testCtx->localDeclarations().size(), 1); IndexedDeclaration C_test_v1 = testCtx->localDeclarations()[0]; QCOMPARE(testCtx->childContexts().size(), 2); DUContext* child = testCtx->childContexts()[1]; QCOMPARE(child->localDeclarations().size(), 2); IndexedDeclaration C_test_v2 = child->localDeclarations()[0]; IndexedDeclaration C_test_v3 = child->localDeclarations()[1]; QCOMPARE(C_test_v1.declaration()->identifier(), Identifier("v1")); QCOMPARE(C_test_v2.declaration()->identifier(), Identifier("v2")); QCOMPARE(C_test_v3.declaration()->identifier(), Identifier("v3")); QCOMPARE(C_m1.declaration()->identifier(), Identifier("m1")); QCOMPARE(C_m2.declaration()->identifier(), Identifier("m2")); QCOMPARE(C_test.declaration()->identifier(), Identifier("test")); QCOMPARE(CDecl.declaration()->identifier(), Identifier("C")); QCOMPARE(ADecl.declaration()->identifier(), Identifier("A")); lock.unlock(); code1.m_insertedCode.setText("struct A { struct Member2; struct Member1; };"); code3.m_insertedCode.setText("#include \n class Q{}; struct C : public A { Member2 m2; int c; A test(int arg) { int w1; int v1;\n\n { int *v3; }} int test(); };"); code3.parse(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::ForceUpdateRecursive, true); lock.lock(); QVERIFY(ADecl.declaration()); QCOMPARE(ADecl.declaration()->identifier(), Identifier("A")); QVERIFY(CDecl.declaration()); QCOMPARE(CDecl.declaration()->identifier(), Identifier("C")); QVERIFY(!C_m1.declaration()); QVERIFY(C_m2.declaration()); QCOMPARE(C_m2.declaration()->identifier(), Identifier("m2")); QVERIFY(C_test.declaration()); QCOMPARE(C_test.declaration()->identifier(), Identifier("test")); QVERIFY(C_test_v1.declaration()); QCOMPARE(C_test_v1.declaration()->identifier(), Identifier("v1")); QVERIFY(!C_test_v2.declaration()); QVERIFY(C_test_v3.declaration()); QCOMPARE(C_test_v3.declaration()->identifier(), Identifier("v3")); } } void TestCppCodegen::testSimplifiedUpdating() { { InsertIntoDUChain code1("duchaintest_1.h", "template struct test{};" "template <> struct test {};"); code1.parse(TopDUContext::SimplifiedVisibleDeclarationsAndContexts | TopDUContext::AST); DUChainReadLocker lock; //No specializations are handled in simplified parsing Cpp::TemplateDeclaration *specClassDecl = dynamic_cast(code1->localDeclarations()[1]); QVERIFY(!specClassDecl->specializedFrom().data()); } { InsertIntoDUChain code1("duchaintest_1.h", "struct A { struct Member2; struct Member1; };"); InsertIntoDUChain code3("duchaintest_3.h", "#include \n struct C : public A { Member1 m1; Member2 m2; };"); kWarning() << "********************* Parsing step 1"; code3.parse(TopDUContext::AllDeclarationsContextsUsesAndAST); DUChainReadLocker lock; QCOMPARE(code3->localDeclarations().size(), 1); QCOMPARE(code3->childContexts().size(), 1); QCOMPARE(code3->childContexts()[0]->localDeclarations().size(), 2); QVERIFY(code3->childContexts()[0]->localDeclarations()[0]->abstractType().cast()); QVERIFY(code3->childContexts()[0]->localDeclarations()[1]->abstractType().cast()); QCOMPARE(code3->childContexts()[0]->importedParentContexts().size(), 1); QCOMPARE(code1->childContexts().size(), 1); } { InsertIntoDUChain code1("duchaintest_1.h", "struct A { struct Member1; };"); InsertIntoDUChain code2("duchaintest_2.h", "template struct B : public T{ struct Member2; };"); InsertIntoDUChain code3("duchaintest_3.h", "#include \n #include \n struct C : public B { Member1 m1; Member2 m2; };"); kWarning() << "********************* Parsing step 1"; code3.parse(TopDUContext::AllDeclarationsContextsUsesAndAST); DUChainReadLocker lock; QCOMPARE(code3->localDeclarations().size(), 1); QCOMPARE(code3->childContexts().size(), 1); QCOMPARE(code3->childContexts()[0]->localDeclarations().size(), 2); QVERIFY(code3->childContexts()[0]->localDeclarations()[0]->abstractType().cast()); QVERIFY(code3->childContexts()[0]->localDeclarations()[1]->abstractType().cast()); QCOMPARE(code3->childContexts()[0]->importedParentContexts().size(), 1); QCOMPARE(code1->childContexts().size(), 1); QCOMPARE(code2->localDeclarations().size(), 1); ClassDeclaration* BClass = dynamic_cast(code2->localDeclarations()[0]); QVERIFY(BClass); QCOMPARE(BClass->baseClassesSize(), 1u); QCOMPARE(BClass->baseClasses()[0].baseClass.abstractType()->toString(), QString("T")); QCOMPARE(code3->childContexts()[0]->importedParentContexts().size(), 1); DUContext* BAContext = code3->childContexts()[0]->importedParentContexts()[0].context(code3.topContext()); QVERIFY(BAContext); QVERIFY(!BAContext->inSymbolTable()); //2 contexts are imported: The template-context and the parent-class context QCOMPARE(BAContext->importedParentContexts().size(), 2); QCOMPARE(BAContext->importedParentContexts()[1].context(code3.topContext()), code1->childContexts()[0]); ClassDeclaration* classDecl = dynamic_cast(BAContext->owner()); QVERIFY(classDecl); QCOMPARE(classDecl->baseClassesSize(), 1u); QCOMPARE(classDecl->baseClasses()[0].baseClass.index(), code1->localDeclarations()[0]->indexedType().index()); lock.unlock(); kWarning() << "********************* Parsing step 2"; code3.parse(TopDUContext::AllDeclarationsContextsUsesAndAST | TopDUContext::ForceUpdateRecursive, true); lock.lock(); QCOMPARE(code3->localDeclarations().size(), 1); QCOMPARE(code3->childContexts().size(), 1); QCOMPARE(code3->childContexts()[0]->localDeclarations().size(), 2); QCOMPARE(code1->childContexts().size(), 1); QVERIFY(code3->childContexts()[0]->localDeclarations()[0]->abstractType().cast()); QVERIFY(code3->childContexts()[0]->localDeclarations()[1]->abstractType().cast()); //BClass should have been updated, not deleted QVERIFY(BClass == dynamic_cast(code2->localDeclarations()[0])); QCOMPARE(BClass->baseClassesSize(), 1u); QCOMPARE(BClass->baseClasses()[0].baseClass.abstractType()->toString(), QString("T")); //The template-instantiation context "B" should have been deleted DUContext* BAContext2 = code3->childContexts()[0]->importedParentContexts()[0].context(code3.topContext()); // kDebug() << "BAContexts" << BAContext << BAContext2; // QVERIFY(BAContext != BAContext2); QCOMPARE(BAContext2->importedParentContexts().size(), 2); QCOMPARE(BAContext2->importedParentContexts()[1].context(code3.topContext()), code1->childContexts()[0]); classDecl = dynamic_cast(BAContext2->owner()); QVERIFY(classDecl); QCOMPARE(classDecl->baseClassesSize(), 1u); QCOMPARE(classDecl->baseClasses()[0].baseClass.index(), code1->localDeclarations()[0]->indexedType().index()); } { InsertIntoDUChain code1("duchaintest_1.h", "struct A { struct Member1; };"); InsertIntoDUChain code2("duchaintest_2.h", "template struct B : public T{ struct Member2; };"); InsertIntoDUChain code3("duchaintest_3.h", "#include \n #include \n typedef B Parent; struct C : public Parent { Member1 m1; Member2 m2; };"); code3.parse(TopDUContext::AllDeclarationsContextsUsesAndAST); DUChainReadLocker lock; QCOMPARE(code3->localDeclarations().size(), 2); QCOMPARE(code3->childContexts().size(), 1); QCOMPARE(code3->childContexts()[0]->localDeclarations().size(), 2); QVERIFY(code3->childContexts()[0]->localDeclarations()[0]->abstractType().cast()); QVERIFY(code3->childContexts()[0]->localDeclarations()[1]->abstractType().cast()); lock.unlock(); code3.parse(TopDUContext::AllDeclarationsContextsUsesAndAST | TopDUContext::ForceUpdateRecursive, true); lock.lock(); QCOMPARE(code3->localDeclarations().size(), 2); QCOMPARE(code3->childContexts().size(), 1); QCOMPARE(code3->childContexts()[0]->localDeclarations().size(), 2); QVERIFY(code3->childContexts()[0]->localDeclarations()[0]->abstractType().cast()); QVERIFY(code3->childContexts()[0]->localDeclarations()[1]->abstractType().cast()); } { QString text = "class C { class D d; };"; InsertIntoDUChain code("testsimplified.cpp", text); code.parse(TopDUContext::AllDeclarationsContextsUsesAndAST); DUChainReadLocker lock; dumpAST(code); dumpDUChain(code); //The forward-declaration of 'D' is forwarded into the top-context QCOMPARE(code->localDeclarations().size(), 2); Declaration* classDecl = code->localDeclarations()[0]; QCOMPARE(code->childContexts().size(), 1); QCOMPARE(code->childContexts()[0]->localDeclarations().size(), 1); QVERIFY(code->childContexts()[0]->localDeclarations()[0]->abstractType().cast()); QCOMPARE(code->childContexts()[0]->localDeclarations()[0]->abstractType()->toString(), QString("D")); lock.unlock(); code.parse(TopDUContext::AllDeclarationsContextsUsesAndAST | TopDUContext::ForceUpdate, true); lock.lock(); dumpDUChain(code); QCOMPARE(code->localDeclarations().size(), 2); //Verify that an update has happened, rather than recreating everything QCOMPARE(code->localDeclarations()[0], classDecl); QCOMPARE(code->childContexts().size(), 1); QCOMPARE(code->childContexts()[0]->localDeclarations().size(), 1); QVERIFY(code->childContexts()[0]->localDeclarations()[0]->abstractType().cast()); QCOMPARE(code->childContexts()[0]->localDeclarations()[0]->abstractType()->toString(), QString("D")); } { QString text = "class C {int test(); int mem; }; void test(int a); int i;"; InsertIntoDUChain code("testsimplified.cpp", text); code.parse(TopDUContext::SimplifiedVisibleDeclarationsAndContexts | TopDUContext::AST); DUChainReadLocker lock; dumpAST(code); dumpDUChain(code); QCOMPARE(code->localDeclarations().size(), 3); QCOMPARE(code->childContexts().size(), 1); QCOMPARE(code->childContexts()[0]->localDeclarations().size(), 2); QVERIFY(!code->childContexts()[0]->localDeclarations()[0]->abstractType()); QVERIFY(!code->childContexts()[0]->localDeclarations()[1]->abstractType()); //In simplified parsing mode, the type should not have been built QVERIFY(!code->localDeclarations()[0]->abstractType()); QVERIFY(code->localDeclarations()[0]->kind() == Declaration::Type); QVERIFY(!code->localDeclarations()[1]->abstractType()); QVERIFY(code->localDeclarations()[1]->kind() == Declaration::Instance); QVERIFY(!code->localDeclarations()[2]->abstractType()); QVERIFY(code->localDeclarations()[2]->kind() == Declaration::Instance); { uint count; const KDevelop::CodeModelItem* items; KDevelop::CodeModel::self().items(code->url(), count, items); for(uint a = 0; a < count; ++a) { if(items[a].id == code->localDeclarations()[0]->qualifiedIdentifier()) { QVERIFY(items[a].kind & KDevelop::CodeModelItem::Class); } } } } { InsertIntoDUChain codeA("A.h", "#ifndef A_H\n #define A_H\n class A{}; \n#endif"); InsertIntoDUChain codeB("B.h", "#include \n class B{};"); codeB.parse(TopDUContext::SimplifiedVisibleDeclarationsAndContexts | TopDUContext::AST | TopDUContext::Recursive); DUChainReadLocker lock; //This is not only for debug-output, but also verifies that the AST is there as requesed dumpAST(codeA); QCOMPARE(codeB->importedParentContexts().size(), 1); QCOMPARE(codeA->localDeclarations().size(), 1); QVERIFY(codeA->parsingEnvironmentFile()->featuresSatisfied((TopDUContext::Features)(TopDUContext::SimplifiedVisibleDeclarationsAndContexts | TopDUContext::AST | TopDUContext::Recursive))); QVERIFY(codeB->parsingEnvironmentFile()->featuresSatisfied((TopDUContext::Features)(TopDUContext::SimplifiedVisibleDeclarationsAndContexts | TopDUContext::AST | TopDUContext::Recursive))); lock.unlock(); //Update with more features codeB.parse(TopDUContext::AllDeclarationsContextsUsesAndAST | TopDUContext::Recursive, true); lock.lock(); QCOMPARE(codeB->importedParentContexts().size(), 1); QCOMPARE(codeA->localDeclarations().size(), 1); QVERIFY(codeA->parsingEnvironmentFile()->featuresSatisfied((TopDUContext::Features)(TopDUContext::AllDeclarationsContextsUsesAndAST | TopDUContext::Recursive))); QVERIFY(codeB->parsingEnvironmentFile()->featuresSatisfied((TopDUContext::Features)(TopDUContext::AllDeclarationsContextsUsesAndAST | TopDUContext::Recursive))); } { ///Test whether "empty" files work InsertIntoDUChain codeA("Q.h", ""); InsertIntoDUChain codeB("B.h", "#include \n class B{};"); codeB.parse(TopDUContext::SimplifiedVisibleDeclarationsAndContexts | TopDUContext::AST | TopDUContext::Recursive); DUChainReadLocker lock; QCOMPARE(codeB->importedParentContexts().size(), 1); QVERIFY(codeA->localDeclarations().isEmpty()); QVERIFY(!codeA->parsingEnvironmentFile()->isProxyContext()); } { ///Test the 'ignoring' of header-guards InsertIntoDUChain codeA("A.h", "#ifndef A_H\n #define A_H\n class A{};\n #ifdef HONK\n class Honk {};\n #endif\n \n#endif \n"); InsertIntoDUChain codeB("B.h", "#define A_H\n \n #include \n class B{};"); QVERIFY(!codeA.tryGet()); codeB.parse(TopDUContext::SimplifiedVisibleDeclarationsAndContexts | TopDUContext::AST | TopDUContext::Recursive); DUChainReadLocker lock; //This is not only for debug-output, but also verifies that the AST is there as requesed dumpAST(codeA); QCOMPARE(codeA->localDeclarations().size(), 1); QCOMPARE(codeB->importedParentContexts().size(), 1); lock.unlock(); codeB.parse(TopDUContext::SimplifiedVisibleDeclarationsAndContexts | TopDUContext::AST | TopDUContext::ForceUpdateRecursive | TopDUContext::Recursive); lock.lock(); QCOMPARE(codeA->localDeclarations().size(), 1); QCOMPARE(codeB->importedParentContexts().size(), 1); } } void TestCppCodegen::testAstDuChainMapping() { { InsertIntoDUChain code("ClassA.h", "class ClassA { public: ClassA(); private: int i; float f, j;\ struct ContainedStruct { int i; ClassA * p; } structVar; };"); code.parse(TopDUContext::AllDeclarationsContextsUsesAndAST); DUChainReadLocker lock; //----ClassA.h---- ParseSession::Ptr session = ParseSession::Ptr::dynamicCast(code->ast()); QVERIFY(session); TranslationUnitAST * ast = session->topAstNode(); QVERIFY(ast); QVERIFY(ast->declarations); QCOMPARE(ast->declarations->count(), 1); QVERIFY(AstUtils::childNode(ast, 0)); QCOMPARE(AstUtils::childNode(ast, 0)->type_specifier, session->astNodeFromDeclaration(code->localDeclarations()[0])); //ClassA ClassSpecifierAST * classAst = AstUtils::node_cast (AstUtils::childNode(ast, 0)->type_specifier); QVERIFY(classAst); DUContext * cont = code->localDeclarations()[0]->internalContext(); QVERIFY(cont); QCOMPARE(classAst->member_specs->count(), 6); QCOMPARE(cont->localDeclarations().size(), 6); //ClassA() QCOMPARE(AstUtils::childNode(classAst, 1), session->astNodeFromDeclaration(cont->localDeclarations()[0])); //int i QCOMPARE(AstUtils::childNode(classAst, 3), session->astNodeFromDeclaration(cont->localDeclarations()[1])); //float f QCOMPARE(AstUtils::childNode(classAst, 4), session->astNodeFromDeclaration(cont->localDeclarations()[2])); //float j, part of the same declaration as above QCOMPARE(AstUtils::childNode(classAst, 4), session->astNodeFromDeclaration(cont->localDeclarations()[3])); //struct ContainedStruct QCOMPARE(AstUtils::childNode(classAst, 5)->type_specifier, session->astNodeFromDeclaration(cont->localDeclarations()[4])); //ContainedStruct structVar, part of the same declaration as above QCOMPARE(AstUtils::childNode(classAst, 5), session->astNodeFromDeclaration(cont->localDeclarations()[5])); } { //----ClassA.cpp---- InsertIntoDUChain codeH("ClassA.h", "class ClassA { public: ClassA(); private: int i; float f, j;\ struct ContainedStruct { int i; ClassA * p; } structVar; };"); InsertIntoDUChain code("ClassA.cpp", "#include \n ClassA::ClassA() : i(0), j(0.0) {structVar.i = 0; ContainedStruct testStruct; }"); code.parse(TopDUContext::AllDeclarationsContextsUsesAndAST); DUChainReadLocker lock; ParseSession::Ptr session = ParseSession::Ptr::dynamicCast(code->ast()); QVERIFY(session); TranslationUnitAST * ast = session->topAstNode(); QVERIFY(ast); QVERIFY(ast->declarations); QCOMPARE(ast->declarations->count(), 1); QVERIFY(AstUtils::childNode(ast, 0)); QCOMPARE(AstUtils::childNode(ast, 0), session->astNodeFromDeclaration(KDevelop::DeclarationPointer(code->localDeclarations()[0]))); QCOMPARE(code->localDeclarations()[0]->context()->importedParentContexts().size(), 1); QVERIFY(code->localDeclarations()[0]->context()->importedParentContexts()[0].context(code.m_topContext) != code.m_topContext); } { InsertIntoDUChain code("AbstractClass.h", "class AbstractClass { public: virtual ~AbstractClass();\ virtual void pureVirtual() = 0; virtual const int constPure(const int &) const = 0; \ virtual void regularVirtual(); virtual const int constVirtual(const int &) const; int data; };"); code.parse(TopDUContext::AllDeclarationsContextsUsesAndAST); DUChainReadLocker lock; //----AbstractClass.h---- ParseSession::Ptr session = ParseSession::Ptr::dynamicCast(code->ast()); QVERIFY(session); TranslationUnitAST * ast = session->topAstNode(); QVERIFY(ast); QVERIFY(ast->declarations); QCOMPARE(ast->declarations->count(), 1); QVERIFY(AstUtils::childNode(ast, 0)); QCOMPARE(session->astNodeFromDeclaration(code->localDeclarations()[0]), AstUtils::childNode(ast, 0)->type_specifier); //AbstractClass ClassSpecifierAST * classAst = AstUtils::node_cast (AstUtils::childNode(ast, 0)->type_specifier); QVERIFY(classAst); DUContext * cont = code->localDeclarations()[0]->internalContext(); QVERIFY(cont); //~AbstractClass() QCOMPARE(AstUtils::childNode(classAst, 1), session->astNodeFromDeclaration(cont->localDeclarations()[0])); //pureVirtual() QCOMPARE(AstUtils::childNode(classAst, 2), session->astNodeFromDeclaration(cont->localDeclarations()[1])); //constPure() SimpleDeclarationAST * func = AstUtils::childNode(classAst, 3); QCOMPARE(func, session->astNodeFromDeclaration(cont->localDeclarations()[2])); QVERIFY(AstUtils::childInitDeclarator(func, 0)); QVERIFY(AstUtils::parameterAtIndex(AstUtils::childInitDeclarator(func, 0)->declarator, 0)); QVERIFY(cont->localDeclarations()[2]->internalContext()->localDeclarations()[0]); QVERIFY(AstUtils::parameterAtIndex(AstUtils::childInitDeclarator(func, 0)->declarator, 0)); QCOMPARE(AstUtils::parameterAtIndex(AstUtils::childInitDeclarator(func, 0)->declarator, 0), session->astNodeFromDeclaration(cont->localDeclarations()[2]->internalContext()->localDeclarations()[0])); //regularVirtual() QCOMPARE(AstUtils::childNode(classAst, 4), session->astNodeFromDeclaration(cont->localDeclarations()[3])); //constPure() func = AstUtils::childNode(classAst, 5); QCOMPARE(func, session->astNodeFromDeclaration(cont->localDeclarations()[4])); QVERIFY(AstUtils::childInitDeclarator(func, 0)); QVERIFY(AstUtils::parameterAtIndex(AstUtils::childInitDeclarator(func, 0)->declarator, 0)); QVERIFY(cont->localDeclarations()[4]->internalContext()->localDeclarations()[0]); QVERIFY(AstUtils::parameterAtIndex(AstUtils::childInitDeclarator(func, 0)->declarator, 0)); QCOMPARE(AstUtils::parameterAtIndex(AstUtils::childInitDeclarator(func, 0)->declarator, 0), session->astNodeFromDeclaration(cont->localDeclarations()[4]->internalContext()->localDeclarations()[0])); } } void TestCppCodegen::testMacroDeclarationOrder() { InsertIntoDUChain source("thefile.h", "#define DECLARE_MY_ITERATOR\ template class MyIteratorMacro { T value(); Key key(); };\n\ DECLARE_MY_ITERATOR\n\ template class MyIteratorDirect { Key key(); T value(); };"); source.parse(TopDUContext::AllDeclarationsContextsUsesAndAST); DUChainReadLocker lock; ReferencedTopDUContext top = source.m_topContext; Declaration* macroKey = top->findDeclarations(QualifiedIdentifier("MyIteratorMacro::key")).first(); Declaration* macroVal = top->findDeclarations(QualifiedIdentifier("MyIteratorMacro::value")).first(); Declaration* directKey = top->findDeclarations(QualifiedIdentifier("MyIteratorDirect::key")).first(); Declaration* directVal = top->findDeclarations(QualifiedIdentifier("MyIteratorDirect::value")).first(); QVERIFY(directKey->abstractType()->toString() == "function int ()"); QVERIFY(directVal->abstractType()->toString() == "function char ()"); QVERIFY(macroKey->abstractType()->toString() == "function int ()"); QVERIFY(macroVal->abstractType()->toString() == "function char ()"); } void TestCppCodegen::testMoveIntoSource() { QFETCH(QString, origHeader); QFETCH(QString, origImpl); QFETCH(QString, newHeader); QFETCH(QString, newImpl); QFETCH(QualifiedIdentifier, id); // make sure there aren't any stale queued documents to be parsed QCOMPARE(ICore::self()->languageController()->backgroundParser()->queuedCount(), 0); TestFile header(origHeader, "h"); TestFile impl(origImpl, "cpp", &header); header.parse(KDevelop::TopDUContext::AllDeclarationsContextsAndUses); QVERIFY(header.waitForParsed()); impl.parse(KDevelop::TopDUContext::AllDeclarationsContextsAndUses); QVERIFY(impl.waitForParsed()); ReferencedTopDUContext refTop = header.topContext(); QVERIFY(refTop); IndexedDeclaration declaration; { DUChainReadLocker lock; TopDUContext* top = DUChainUtils::contentContextFromProxyContext(refTop); QList< Declaration* > decls = top->findDeclarations(id); QCOMPARE(decls.size(), 1); declaration = IndexedDeclaration(decls.first()); QVERIFY(declaration.isValid()); } CodeRepresentation::setDiskChangesForbidden(false); SimpleRefactoring *refactoring = new SimpleRefactoring(); QCOMPARE(refactoring->moveIntoSource(declaration), QString()); delete refactoring; CodeRepresentation::setDiskChangesForbidden(true); QCOMPARE(header.fileContents(), newHeader); QCOMPARE(impl.fileContents(), newImpl); // SimpleRefactoring::moveIntoSource re-adds the documents to the BackgroundParser // make sure we don't leave any stale requests for the next tests in the BackgroundParser ICore::self()->languageController()->backgroundParser()->revertAllRequests(0); QCOMPARE(ICore::self()->languageController()->backgroundParser()->queuedCount(), 0); } void TestCppCodegen::testMoveIntoSource_data() { QTest::addColumn("origHeader"); QTest::addColumn("origImpl"); QTest::addColumn("newHeader"); QTest::addColumn("newImpl"); QTest::addColumn("id"); const QualifiedIdentifier fooId("::foo"); /// NOTE: THIS CODE MUST _NOT_ DEPEND ON A SPECIFIC FORMATTER CONFIGURATION! QTest::newRow("globalfunction") << QString("int foo()\n{\n int i = 0;\n return 0;\n}\n") << QString() << QString("int foo();\n") << QString("\nint foo()\n{\n int i = 0;\n return 0;\n}") << fooId; QTest::newRow("staticfunction") << QString("static int foo()\n{\n int i = 0;\n return 0;\n}\n") << QString() << QString("static int foo();\n") << QString("\nint foo()\n{\n int i = 0;\n return 0;\n}") << fooId; QTest::newRow("funcsameline") << QString("int foo() {\n int i = 0;\n return 0;\n}\n") << QString() << QString("int foo();\n") << QString("\nint foo() {\n int i = 0;\n return 0;\n}") << fooId; QTest::newRow("func-comment") << QString("int foo()\n/* foobar */ {\n int i = 0;\n return 0;\n}\n") << QString() << QString("int foo()\n/* foobar */;\n") ///TODO: should the comment be moved as well? << QString("\nint foo() {\n int i = 0;\n return 0;\n}") << fooId; QTest::newRow("func-comment2") << QString("int foo()\n/*asdf*/\n{\n int i = 0;\n return 0;\n}\n") << QString() << QString("int foo()\n/*asdf*/;\n") ///TODO: should the comment be moved as well? << QString("\nint foo()\n{\n int i = 0;\n return 0;\n}") << fooId; const QualifiedIdentifier aFooId("a::foo"); QTest::newRow("class-method") << QString("class a {\n int foo(){\n return 0;\n }\n};\n") << QString() << QString("class a {\n int foo();\n};\n") << QString("\nint a::foo() {\n return 0;\n }") << aFooId; QTest::newRow("class-method-const") << QString("class a {\n int foo() const\n {\n return 0;\n }\n};\n") << QString() << QString("class a {\n int foo() const;\n};\n") << QString("\nint a::foo() const\n {\n return 0;\n }") << aFooId; QTest::newRow("class-method-const-sameline") << QString("class a {\n int foo() const{\n return 0;\n }\n};\n") << QString() << QString("class a {\n int foo() const;\n};\n") << QString("\nint a::foo() const {\n return 0;\n }") << aFooId; } #include "moc_test_cppcodegen.cpp"