mirror of
https://bitbucket.org/smil3y/kde-extraapps.git
synced 2025-02-26 20:03:10 +00:00
3835 lines
184 KiB
C++
3835 lines
184 KiB
C++
/* This file is part of KDevelop
|
|
Copyright 2006 Hamish Rodda<rodda@kde.org>
|
|
Copyright 2007-2009 David Nolden <david.nolden.kdevelop@art-master.de>
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License version 2 as published by the Free Software Foundation.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "test_cppcodecompletion.h"
|
|
|
|
#include <QtTest/QtTest>
|
|
#include <typeinfo>
|
|
|
|
#include <language/duchain/duchain.h>
|
|
#include <language/duchain/duchainlock.h>
|
|
#include <language/duchain/topducontext.h>
|
|
#include <language/duchain/forwarddeclaration.h>
|
|
#include <language/duchain/declaration.h>
|
|
#include <language/duchain/aliasdeclaration.h>
|
|
#include <language/editor/documentrange.h>
|
|
#include <language/duchain/classfunctiondeclaration.h>
|
|
#include <language/duchain/dumpchain.h>
|
|
#include "declarationbuilder.h"
|
|
#include "usebuilder.h"
|
|
#include "cppeditorintegrator.h"
|
|
#include "dumptypes.h"
|
|
#include "environmentmanager.h"
|
|
|
|
#include "tokens.h"
|
|
#include "parsesession.h"
|
|
|
|
#include "rpp/preprocessor.h"
|
|
#include "rpp/pp-engine.h"
|
|
#include "rpp/pp-environment.h"
|
|
#include "expressionvisitor.h"
|
|
#include "expressionparser.h"
|
|
#include "codecompletion/context.h"
|
|
#include "codecompletion/helpers.h"
|
|
#include "codecompletion/item.h"
|
|
#include "codecompletion/implementationhelperitem.h"
|
|
#include "cpppreprocessenvironment.h"
|
|
#include <language/duchain/classdeclaration.h>
|
|
#include "cppduchain/missingdeclarationproblem.h"
|
|
#include "cppduchain/missingdeclarationassistant.h"
|
|
#include "overloadresolutionhelper.h"
|
|
#include <language/duchain/functiondefinition.h>
|
|
|
|
#include <language/codecompletion/codecompletiontesthelper.h>
|
|
#include <language/duchain/persistentsymboltable.h>
|
|
#include <cpputils.h>
|
|
|
|
#include <tests/autotestshell.h>
|
|
#include <tests/testcore.h>
|
|
|
|
#include <KTempDir>
|
|
#include <KTextEditor/Editor>
|
|
#include <KTextEditor/EditorChooser>
|
|
#include <KTextEditor/View>
|
|
#include <qtest_kde.h>
|
|
|
|
using namespace KTextEditor;
|
|
|
|
using namespace KDevelop;
|
|
|
|
QTEST_KDEMAIN(TestCppCodeCompletion, GUI)
|
|
|
|
QString testFile1 = "class Erna; struct Honk { int a,b; enum Enum { Number1, Number2 }; Erna& erna; operator int() {}; }; struct Pointer { Honk* operator ->() const {}; Honk& operator * () {}; }; Honk globalHonk; Honk honky; \n#define HONK Honk\n";
|
|
|
|
QString testFile2 = "struct Honk { int a,b; enum Enum { Number1, Number2 }; Erna& erna; operator int() {}; }; struct Erna { Erna( const Honk& honk ) {} }; struct Heinz : public Erna {}; Erna globalErna; Heinz globalHeinz; int globalInt; Heinz globalFunction(const Heinz& h ) {}; Erna globalFunction( const Erna& erna); Honk globalFunction( const Honk&, const Heinz& h ) {}; int globalFunction(int ) {}; HONK globalMacroHonk; struct GlobalClass { Heinz function(const Heinz& h ) {}; Erna function( const Erna& erna); }; GlobalClass globalClass;\n#undef HONK\n";
|
|
|
|
QString testFile3 = "struct A {}; struct B : public A {};";
|
|
|
|
QString testFile4 = "void test1() {}; class TestClass() { TestClass() {} };";
|
|
|
|
typedef CodeCompletionItemTester<Cpp::CodeCompletionContext> CompletionItemTester;
|
|
|
|
#define TEST_FILE_PARSE_ONLY if (testFileParseOnly) QSKIP("Skip", SkipSingle);
|
|
TestCppCodeCompletion::TestCppCodeCompletion()
|
|
{
|
|
testFileParseOnly = false;
|
|
}
|
|
|
|
void TestCppCodeCompletion::initTestCase()
|
|
{
|
|
AutoTestShell::init(QStringList() << "kdevcppsupport");
|
|
TestCore::initialize(Core::NoUi);
|
|
Cpp::EnvironmentManager::init();
|
|
|
|
DUChain::self()->disablePersistentStorage();
|
|
typeInt = AbstractType::Ptr(new IntegralType(IntegralType::TypeInt));
|
|
|
|
addInclude( "testFile1.h", testFile1 );
|
|
addInclude( "testFile2.h", testFile2 );
|
|
addInclude( "testFile3.h", testFile3 );
|
|
}
|
|
|
|
void TestCppCodeCompletion::cleanupTestCase()
|
|
{
|
|
TestCore::shutdown();
|
|
}
|
|
|
|
Declaration* TestCppCodeCompletion::findDeclaration(DUContext* context, const Identifier& id, const CursorInRevision& position)
|
|
{
|
|
QList<Declaration*> ret = context->findDeclarations(id, position);
|
|
if (ret.count())
|
|
return ret.first();
|
|
return 0;
|
|
}
|
|
|
|
Declaration* TestCppCodeCompletion::findDeclaration(DUContext* context, const QualifiedIdentifier& id, const CursorInRevision& position)
|
|
{
|
|
QList<Declaration*> ret = context->findDeclarations(id, position);
|
|
if (ret.count())
|
|
return ret.first();
|
|
return 0;
|
|
}
|
|
|
|
void TestCppCodeCompletion::testCommentClearing()
|
|
{
|
|
QByteArray method = "struct a { int i; }; int foo() { a inst; }";
|
|
TopDUContext* top = parse(method, DumpNone);
|
|
int ctxt = 2;
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
CompletionItemTester test(top->childContexts()[ctxt], "//* \n inst.");
|
|
QCOMPARE(test.names, QStringList() << "i");
|
|
CompletionItemTester test2(top->childContexts()[ctxt], "// \n inst.");
|
|
QCOMPARE(test2.names, QStringList() << "i");
|
|
CompletionItemTester test3(top->childContexts()[ctxt], "/*//*/ inst.");
|
|
QCOMPARE(test3.names, QStringList() << "i");
|
|
CompletionItemTester test4(top->childContexts()[ctxt], " ///*//*/ \n inst.");
|
|
QCOMPARE(test4.names, QStringList() << "i");
|
|
CompletionItemTester test5(top->childContexts()[ctxt], "/*// inst.");
|
|
QCOMPARE(test5.names, QStringList());
|
|
CompletionItemTester test6(top->childContexts()[ctxt], "// inst.");
|
|
QCOMPARE(test6.names, QStringList());
|
|
CompletionItemTester test7(top->childContexts()[ctxt], "/*// \n/* // \n */ inst.");
|
|
QCOMPARE(test7.names, QStringList() << "i");
|
|
CompletionItemTester test8(top->childContexts()[ctxt], "// \n /*// \n/* // \n */ /*/*/ inst.");
|
|
QCOMPARE(test8.names, QStringList() << "i");
|
|
release(top);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testExpressionBefore()
|
|
{
|
|
QString exp1 = "int x";
|
|
QCOMPARE(Cpp::expressionBefore(exp1, exp1.length()), 4);
|
|
QString exp2 = "x = x().";
|
|
QCOMPARE(Cpp::expressionBefore(exp2, exp2.length()), 4);
|
|
QString exp3 = "x = (x().)";
|
|
QCOMPARE(Cpp::expressionBefore(exp3, exp3.length()), 4);
|
|
QString exp4 = "x = ((x().))";
|
|
QCOMPARE(Cpp::expressionBefore(exp4, exp4.length()), 4);
|
|
QString exp5 = "x = asdfasdf";
|
|
QCOMPARE(Cpp::expressionBefore(exp5, exp5.length()), 4);
|
|
QString exp6 = "++asdfasdf";
|
|
QCOMPARE(Cpp::expressionBefore(exp6, exp6.length()), 2);
|
|
QString exp7 = "(asd)";
|
|
QCOMPARE(Cpp::expressionBefore(exp7, exp7.length()), 0);
|
|
QString exp8 = "x = x[]->";
|
|
QCOMPARE(Cpp::expressionBefore(exp8, exp8.length()), 4);
|
|
QString exp9 = "x = x()::";
|
|
QCOMPARE(Cpp::expressionBefore(exp9, exp9.length()), 4);
|
|
QString exp10 = "x = x.y.z->x::a[]->h()[q]::S<n>";
|
|
QCOMPARE(Cpp::expressionBefore(exp10, exp10.length()), 4);
|
|
QString exp11 = "x >";
|
|
QCOMPARE(Cpp::expressionBefore(exp11, exp11.length()), 3);
|
|
QString exp12 = "x = *(*y)";
|
|
QCOMPARE(Cpp::expressionBefore(exp12, exp12.length()), 5);
|
|
QString exp13 = "q > xyz";
|
|
QCOMPARE(Cpp::expressionBefore(exp13, exp13.length()), 4);
|
|
QString exp14 = "q>x()[asdf].";
|
|
QCOMPARE(Cpp::expressionBefore(exp14, exp14.length()), 2);
|
|
QString exp15 = "x = y(\"(\")";
|
|
QCOMPARE(Cpp::expressionBefore(exp15, exp15.length()), 4);
|
|
QString exp16 = "x = y<\"<\">";
|
|
QCOMPARE(Cpp::expressionBefore(exp16, exp16.length()), 4);
|
|
QString exp17 = "x = y[z(\"[\")]";
|
|
QCOMPARE(Cpp::expressionBefore(exp17, exp17.length()), 4);
|
|
QString exp18 = "x = y(";
|
|
QCOMPARE(Cpp::expressionBefore(exp18, exp18.length()), 6);
|
|
QString exp19 = "x = y[";
|
|
QCOMPARE(Cpp::expressionBefore(exp19, exp19.length()), 6);
|
|
QString exp20 = "x = y<";
|
|
QCOMPARE(Cpp::expressionBefore(exp20, exp20.length()), 6);
|
|
QString exp21 = " ";
|
|
QCOMPARE(Cpp::expressionBefore(exp21, exp21.length()), 1);
|
|
QString exp22 = "";
|
|
QCOMPARE(Cpp::expressionBefore(exp22, exp22.length()), 0);
|
|
QString exp23 = " ";
|
|
QCOMPARE(Cpp::expressionBefore(exp23, exp23.length()), 3);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testSpecialItems()
|
|
{
|
|
QByteArray method = "enum Color { Red = 0, Green = 1, Blue = 2 }; void test(Color c) { }";
|
|
TopDUContext* top = parse(method, DumpNone);
|
|
int ctxt = 2;
|
|
//There is duplication here, but in this case we want all the enum values added in their own group here
|
|
//It's probably not worth it to go through the list and remove the previously added enums when in scope
|
|
const QStringList enumGroupCompletions(QStringList() << "Red" << "Green" << "Blue");
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
CompletionItemTester test(top->childContexts()[ctxt], "c = ");
|
|
qDebug() << "actual names:::" << test.names;
|
|
QCOMPARE(test.names, QStringList() << "Color c =" << "c" << "Color" << "test" << "Red" << "Green" << "Blue" << enumGroupCompletions);
|
|
CompletionItemTester test2(top->childContexts()[ctxt], "test(");
|
|
QCOMPARE(test2.names, QStringList() << "test" << "c" << "Color" << "test" << "Red" << "Green" << "Blue" << enumGroupCompletions);
|
|
CompletionItemTester test3(top->childContexts()[ctxt], "if (c == ");
|
|
QCOMPARE(test3.names, QStringList() << "Color c ==" << "c" << "Color" << "test" << "Red" << "Green" << "Blue" << enumGroupCompletions);
|
|
CompletionItemTester test4(top->childContexts()[ctxt], "if (c > ");
|
|
QCOMPARE(test4.names, QStringList() << "Color c >" << "c" << "Color" << "test" << "Red" << "Green" << "Blue" << enumGroupCompletions);
|
|
CompletionItemTester test5(top->childContexts()[ctxt], "c -= ");
|
|
QCOMPARE(test5.names, QStringList() << "Color c -=" << "c" << "Color" << "test" << "Red" << "Green" << "Blue" << enumGroupCompletions);
|
|
release(top);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testOnlyShow()
|
|
{
|
|
QByteArray method = "template<class T1> class T { }; namespace A { struct B {}; } struct C { }; int testMe() { }";
|
|
TopDUContext* top = parse(method, DumpAll);
|
|
int fctxt = 5;
|
|
int sctxt = 3;
|
|
int nctxt = 2;
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
|
|
CompletionItemTester implementationHelpers(top, "void ");
|
|
QVERIFY(implementationHelpers.completionContext->onlyShow() == Cpp::CodeCompletionContext::ShowImplementationHelpers);
|
|
|
|
CompletionItemTester showTypes(top->childContexts()[fctxt], "A::B* b = new ");
|
|
QVERIFY(showTypes.completionContext->onlyShow() == Cpp::CodeCompletionContext::ShowTypes);
|
|
CompletionItemTester showTypes2(top->childContexts()[fctxt], "A::B* b = const_cast< ");
|
|
QVERIFY(showTypes2.completionContext->onlyShow() == Cpp::CodeCompletionContext::ShowTypes);
|
|
CompletionItemTester showTypes3(top->childContexts()[fctxt], "A::B* b = static_cast< ");
|
|
QVERIFY(showTypes3.completionContext->onlyShow() == Cpp::CodeCompletionContext::ShowTypes);
|
|
CompletionItemTester showTypes4(top->childContexts()[fctxt], "A::B* b = dynamic_cast< ");
|
|
QVERIFY(showTypes4.completionContext->onlyShow() == Cpp::CodeCompletionContext::ShowTypes);
|
|
CompletionItemTester showTypes5(top->childContexts()[fctxt], "A::B* b = reinterpret_cast< ");
|
|
QVERIFY(showTypes5.completionContext->onlyShow() == Cpp::CodeCompletionContext::ShowTypes);
|
|
CompletionItemTester showTypes6(top->childContexts()[fctxt], "const ");
|
|
QVERIFY(showTypes6.completionContext->onlyShow() == Cpp::CodeCompletionContext::ShowTypes);
|
|
CompletionItemTester showTypes7(top, "typedef ");
|
|
QVERIFY(showTypes7.completionContext->onlyShow() == Cpp::CodeCompletionContext::ShowTypes);
|
|
CompletionItemTester showTypes8(top->childContexts()[sctxt], "public ");
|
|
QVERIFY(showTypes8.completionContext->onlyShow() == Cpp::CodeCompletionContext::ShowTypes);
|
|
CompletionItemTester showTypes9(top->childContexts()[sctxt], "protected ");
|
|
QVERIFY(showTypes9.completionContext->onlyShow() == Cpp::CodeCompletionContext::ShowTypes);
|
|
CompletionItemTester showTypes10(top->childContexts()[sctxt], "private ");
|
|
QVERIFY(showTypes10.completionContext->onlyShow() == Cpp::CodeCompletionContext::ShowTypes);
|
|
CompletionItemTester showTypes11(top->childContexts()[sctxt], "virtual ");
|
|
QVERIFY(showTypes11.completionContext->onlyShow() == Cpp::CodeCompletionContext::ShowTypes);
|
|
|
|
CompletionItemTester showTypes12(top->childContexts()[fctxt], "T<");
|
|
QVERIFY(showTypes12.completionContext->onlyShow() == Cpp::CodeCompletionContext::ShowTypes);
|
|
|
|
CompletionItemTester showTypes13(top, "");
|
|
QVERIFY(showTypes13.completionContext->onlyShow() == Cpp::CodeCompletionContext::ShowTypes);
|
|
CompletionItemTester showTypes14(top->childContexts()[sctxt], "");
|
|
QVERIFY(showTypes14.completionContext->onlyShow() == Cpp::CodeCompletionContext::ShowTypes);
|
|
CompletionItemTester showTypes15(top->childContexts()[nctxt], "");
|
|
QVERIFY(showTypes15.completionContext->onlyShow() == Cpp::CodeCompletionContext::ShowTypes);
|
|
|
|
CompletionItemTester dontShowTypes(top, "int i = ");
|
|
QVERIFY(dontShowTypes.completionContext->onlyShow() == Cpp::CodeCompletionContext::ShowAll);
|
|
CompletionItemTester dontShowTypes2(top->childContexts()[sctxt], "int i = ");
|
|
QVERIFY(dontShowTypes2.completionContext->onlyShow() == Cpp::CodeCompletionContext::ShowAll);
|
|
CompletionItemTester dontShowTypes3(top->childContexts()[nctxt], "int i = ");
|
|
QVERIFY(dontShowTypes3.completionContext->onlyShow() == Cpp::CodeCompletionContext::ShowAll);
|
|
CompletionItemTester dontShowTypes4(top, "int i = i +");
|
|
QVERIFY(dontShowTypes4.completionContext->onlyShow() == Cpp::CodeCompletionContext::ShowAll);
|
|
CompletionItemTester dontShowTypes5(top->childContexts()[sctxt], "int i = i +");
|
|
QVERIFY(dontShowTypes5.completionContext->onlyShow() == Cpp::CodeCompletionContext::ShowAll);
|
|
CompletionItemTester dontShowTypes6(top->childContexts()[nctxt], "int i = i +");
|
|
QVERIFY(dontShowTypes6.completionContext->onlyShow() == Cpp::CodeCompletionContext::ShowAll);
|
|
|
|
release(top);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testFriends()
|
|
{
|
|
QByteArray method = "class Friendly{};";
|
|
TopDUContext* top = parse(method, DumpNone);
|
|
int ctxt = 0;
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
CompletionItemTester test(top->childContexts()[ctxt], "friend ");
|
|
QVERIFY(test.completionContext->isValid());
|
|
QVERIFY(test.completionContext->onlyShow() == Cpp::CodeCompletionContext::ShowTypes);
|
|
CompletionItemTester test2(top->childContexts()[ctxt], "friend class ");
|
|
QVERIFY(test2.completionContext->isValid());
|
|
QVERIFY(test2.completionContext->onlyShow() == Cpp::CodeCompletionContext::ShowTypes);
|
|
CompletionItemTester test3(top->childContexts()[ctxt], "class ");
|
|
QVERIFY(!test3.completionContext->isValid());
|
|
|
|
release(top);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testInvalidContexts()
|
|
{
|
|
QByteArray method = "namespace A { struct B {}; } int testMe() { }";
|
|
TopDUContext* top = parse(method, DumpNone);
|
|
int ctxt = 2;
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
|
|
//TODO: these expressions should generally result in an invalid context, but
|
|
// the ExpressionParser doesn't seem to think that "asdf" is an invalid exp.
|
|
// Either the expressionParser should be fixed, or testContextValidity() in
|
|
// context.cpp should be updated
|
|
|
|
CompletionItemTester invalidExp(top->childContexts()[ctxt], "asdf->");
|
|
//Should be invalid (and is, but not as soon as it should be)
|
|
QVERIFY(!invalidExp.completionContext->isValid());
|
|
|
|
CompletionItemTester invalidExp2(top->childContexts()[ctxt], "asdf.");
|
|
//Should be invalid (and is, but not as soon as it should be)
|
|
QVERIFY(!invalidExp2.completionContext->isValid());
|
|
|
|
CompletionItemTester invalidExp3(top->childContexts()[ctxt], "asdf::");
|
|
//Should be valid in case it's a namespace, but asdf should eval to false
|
|
QVERIFY(invalidExp3.completionContext->isValid());
|
|
|
|
CompletionItemTester invalidExp31(top->childContexts()[ctxt], "A::");
|
|
//Should be valid as namespace, but A should evaluate to false
|
|
QVERIFY(invalidExp31.completionContext->isValid());
|
|
|
|
CompletionItemTester invalidExp4(top->childContexts()[ctxt], "asdf(");
|
|
//kept valid for MissingIncludeCompletion, but asdf should eval to false
|
|
QVERIFY(invalidExp4.completionContext->parentContext()->isValid());
|
|
|
|
CompletionItemTester invalidExp5(top->childContexts()[ctxt], "asdf<");
|
|
//TODO: testContextValidity() says templateAccess should be kept valid even with invalid expression
|
|
// as it could get MissingIncludeCompletion, but this is considered a "less than", since an invalid
|
|
// expression will not create a templateAccess context
|
|
QEXPECT_FAIL("", "Test mimics an unknown template expression, but there's no way to differentiate it from a less than operator with an unknown expression, which it gets considered to be. If it were a template, it would be valid.", Continue);
|
|
QVERIFY(invalidExp5.completionContext->parentContext()->isValid());
|
|
|
|
CompletionItemTester invalidExp6(top->childContexts()[ctxt], "asdf &&");
|
|
QVERIFY(!invalidExp6.completionContext->parentContext()->isValid());
|
|
CompletionItemTester invalidExp7(top->childContexts()[ctxt], "void ");
|
|
QVERIFY(!invalidExp7.completionContext->isValid());
|
|
CompletionItemTester invalidExp71(top->childContexts()[ctxt], "int* ");
|
|
QVERIFY(!invalidExp71.completionContext->isValid());
|
|
CompletionItemTester invalidExp8(top->childContexts()[ctxt], "A::B &&");
|
|
QVERIFY(!invalidExp8.completionContext->parentContext()->isValid());
|
|
CompletionItemTester invalidExp9(top->childContexts()[ctxt], ".");
|
|
QVERIFY(!invalidExp9.completionContext->isValid());
|
|
CompletionItemTester invalidExp10(top->childContexts()[ctxt], "->");
|
|
QVERIFY(!invalidExp10.completionContext->isValid());
|
|
CompletionItemTester invalidExp11(top->childContexts()[ctxt], "::");
|
|
QVERIFY(!invalidExp11.completionContext->isValid());
|
|
release(top);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testMemberAccess()
|
|
{
|
|
QByteArray method = "template<class T1, class T2> class T { public: T1 ta(); class U { public: class V{ }; }; };"
|
|
"class X { public: X(){}; int a(int a, T<int,int> b); int b;};"
|
|
"T<int,int> t;"
|
|
"X* getX() { }"
|
|
"class Z { public: Z() = delete; static int a(int b); };";
|
|
TopDUContext* top = parse(method, DumpNone);
|
|
int ctxt = 4;
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
CompletionItemTester testDot(top->childContexts()[ctxt], "t.");
|
|
QCOMPARE(testDot.names, QStringList() << "ta");
|
|
QCOMPARE(testDot.completionContext->accessType(), Cpp::CodeCompletionContext::MemberAccess);
|
|
CompletionItemTester testDot2(top->childContexts()[ctxt], "(*getX()).");
|
|
QCOMPARE(testDot2.names, QStringList() << "a" << "b");
|
|
QCOMPARE(testDot2.completionContext->accessType(), Cpp::CodeCompletionContext::MemberAccess);
|
|
CompletionItemTester testArrow(top->childContexts()[ctxt], "getX()->");
|
|
QCOMPARE(testArrow.names, QStringList() << "a" << "b");
|
|
QCOMPARE(testArrow.completionContext->accessType(), Cpp::CodeCompletionContext::ArrowMemberAccess);
|
|
CompletionItemTester testArrow2(top->childContexts()[ctxt], "(&t)->");
|
|
QCOMPARE(testArrow2.names, QStringList() << "ta");
|
|
QCOMPARE(testArrow2.completionContext->accessType(), Cpp::CodeCompletionContext::ArrowMemberAccess);
|
|
CompletionItemTester testColons(top->childContexts()[ctxt], "X::");
|
|
QCOMPARE(testColons.names, QStringList() << "X" << "a" << "b");
|
|
QCOMPARE(testColons.completionContext->accessType(), Cpp::CodeCompletionContext::StaticMemberChoose);
|
|
CompletionItemTester testColons2(top->childContexts()[ctxt], "T::U::");
|
|
QCOMPARE(testColons2.names, QStringList() << "V");
|
|
QCOMPARE(testColons2.completionContext->accessType(), Cpp::CodeCompletionContext::StaticMemberChoose);
|
|
CompletionItemTester testDeleted(top->childContexts()[ctxt], "Z::");
|
|
QCOMPARE(testDeleted.names, QStringList() << "a");
|
|
QCOMPARE(testDeleted.completionContext->accessType(), Cpp::CodeCompletionContext::StaticMemberChoose);
|
|
release(top);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testParentContexts()
|
|
{
|
|
//Binary operators (also parent contexts) are tested in testBinaryOperators
|
|
QByteArray method = "template<class T1, class T2> class Templ { T1 ta(); T2 tb(); }; class X { X(); int a(int a, Templ<int,int> b); int b;}; X::X() { }";
|
|
TopDUContext* top = parse(method, DumpAll);
|
|
int ctxt = 4;
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
CompletionItemTester templateContext(top->childContexts()[ctxt], "Templ<");
|
|
QCOMPARE(templateContext.names, QStringList() << "Templ" << "Templ" << "X");
|
|
QCOMPARE(templateContext.completionContext->parentContext()->accessType(), Cpp::CodeCompletionContext::TemplateAccess);
|
|
CompletionItemTester templateContext2(top->childContexts()[ctxt], "Templ<int, ");
|
|
QCOMPARE(templateContext2.names, QStringList() << "Templ" << "Templ" << "X");
|
|
QCOMPARE(templateContext2.completionContext->parentContext()->accessType(), Cpp::CodeCompletionContext::TemplateAccess);
|
|
CompletionItemTester templateContext3(top->childContexts()[ctxt], "Templ< int , int > t = new Templ<");
|
|
QCOMPARE(templateContext3.names, QStringList() << "Templ" << "Templ" << "X");
|
|
QCOMPARE(templateContext3.completionContext->parentContext()->accessType(), Cpp::CodeCompletionContext::TemplateAccess);
|
|
CompletionItemTester templateContext4(top->childContexts()[ctxt], "Templ< int , int > t = new Templ<int,");
|
|
QCOMPARE(templateContext4.names, QStringList() << "Templ" << "Templ" << "X");
|
|
QCOMPARE(templateContext4.completionContext->parentContext()->accessType(), Cpp::CodeCompletionContext::TemplateAccess);
|
|
CompletionItemTester templateContext5(top->childContexts()[ctxt], "Templ< int , Templ<");
|
|
QCOMPARE(templateContext5.names, QStringList() << "Templ" << "Templ" << "Templ" << "X");
|
|
QCOMPARE(templateContext5.completionContext->parentContext()->accessType(), Cpp::CodeCompletionContext::TemplateAccess);
|
|
QCOMPARE(templateContext5.completionContext->parentContext()->parentContext()->accessType(), Cpp::CodeCompletionContext::TemplateAccess);
|
|
CompletionItemTester notATemplate(top->childContexts()[ctxt], "if ( a > b < a > this->");
|
|
QCOMPARE(notATemplate.names, QStringList() << "a" << "b");
|
|
QCOMPARE(notATemplate.completionContext->parentContext()->accessType(), Cpp::CodeCompletionContext::BinaryOpFunctionCallAccess);
|
|
CompletionItemTester functionContext(top->childContexts()[ctxt], "a(");
|
|
QCOMPARE(functionContext.names, QStringList() << "a" << "X" << "a" << "b" << "Templ" << "this");
|
|
QCOMPARE(functionContext.completionContext->parentContext()->accessType(), Cpp::CodeCompletionContext::FunctionCallAccess);
|
|
CompletionItemTester functionContext2(top->childContexts()[ctxt], "a(0, ");
|
|
QCOMPARE(functionContext2.names, QStringList() << "a" << "X" << "a" << "b" << "Templ" << "this");
|
|
QCOMPARE(functionContext2.completionContext->parentContext()->accessType(), Cpp::CodeCompletionContext::FunctionCallAccess);
|
|
CompletionItemTester functionContext3(top->childContexts()[ctxt], "a(a(100, ");
|
|
QCOMPARE(functionContext3.names, QStringList() << "a" << "a" << "X" << "a" << "b" << "Templ" << "this");
|
|
QCOMPARE(functionContext3.completionContext->parentContext()->accessType(), Cpp::CodeCompletionContext::FunctionCallAccess);
|
|
CompletionItemTester functionContext4(top->childContexts()[ctxt], "a(a(100, Templ<int,int>), ");
|
|
QCOMPARE(functionContext4.names, QStringList() << "a" << "X" << "a" << "b" << "Templ" << "this");
|
|
QCOMPARE(functionContext4.completionContext->parentContext()->accessType(), Cpp::CodeCompletionContext::FunctionCallAccess);
|
|
//See also testKeywords
|
|
CompletionItemTester returnContext(top->childContexts()[ctxt], "return ");
|
|
QCOMPARE(returnContext.names, QStringList() << "X" << "a" << "b" << "Templ" << "this");
|
|
QCOMPARE(returnContext.completionContext->parentContext()->accessType(), Cpp::CodeCompletionContext::ReturnAccess);
|
|
//See also testCaseContext
|
|
release(top);
|
|
}
|
|
|
|
/*
|
|
* A comment on expect-fails in following 3 tests:
|
|
* "case" statement completion uses DUChainBase::createRangeMoving()->text() to get the switch'd expression
|
|
* and parse its type. Unfortunately, PersistentRangeMoving class itself is not yet completed and doesn't work without an editor.
|
|
* Hence the expression type cannot be resolved, which leads to 2 problems:
|
|
* 1) best matches are not selected, so all items have match quality of 0;
|
|
* 2) specialItemsForArgumentType() which is responsible for fetching enumerators from different scopes doesn't work either.
|
|
*/
|
|
|
|
void TestCppCodeCompletion::testCaseContext()
|
|
{
|
|
QByteArray method = "enum testEnum { foo, bar }; void test() { switch( testEnum ) { } }";
|
|
TopDUContext* top = parse(method, DumpNone);
|
|
int ctxt = 2;
|
|
int sctxt = 1;
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
CompletionItemTester caseContext(top->childContexts()[ctxt]->childContexts()[sctxt], "case ");
|
|
QCOMPARE(caseContext.names, QStringList() << "testEnum" << "foo" << "bar");
|
|
QCOMPARE(caseContext.completionContext->parentContext()->accessType(), Cpp::CodeCompletionContext::CaseAccess);
|
|
QCOMPARE(caseContext.itemData("testEnum", KDevelop::CodeCompletionModel::MatchQuality).toInt(), 0);
|
|
QEXPECT_FAIL("", "PersistentRangeMoving needs to be fixed", Continue);
|
|
QCOMPARE(caseContext.itemData("foo", KDevelop::CodeCompletionModel::MatchQuality).toInt(), 10);
|
|
QEXPECT_FAIL("", "PersistentRangeMoving needs to be fixed", Continue);
|
|
QCOMPARE(caseContext.itemData("bar", KDevelop::CodeCompletionModel::MatchQuality).toInt(), 10);
|
|
release(top);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testCaseContextComplexExpression()
|
|
{
|
|
QByteArray method = "enum testEnum { foo, bar }; struct testStruct { testEnum e; }; void test(testStruct s) { switch (s.e) { } }";
|
|
TopDUContext* top = parse(method, DumpNone);
|
|
int ctxt = 3;
|
|
int sctxt = 1;
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
CompletionItemTester caseContext(top->childContexts()[ctxt]->childContexts()[sctxt], "case ");
|
|
QCOMPARE(caseContext.names, QStringList() << "testEnum" << "testStruct" << "foo" << "bar");
|
|
QCOMPARE(caseContext.completionContext->parentContext()->accessType(), Cpp::CodeCompletionContext::CaseAccess);
|
|
QCOMPARE(caseContext.itemData("testEnum", KDevelop::CodeCompletionModel::MatchQuality).toInt(), 0);
|
|
QCOMPARE(caseContext.itemData("testStruct", KDevelop::CodeCompletionModel::MatchQuality).toInt(), 0);
|
|
QEXPECT_FAIL("", "PersistentRangeMoving needs to be fixed", Continue);
|
|
QCOMPARE(caseContext.itemData("foo", KDevelop::CodeCompletionModel::MatchQuality).toInt(), 10);
|
|
QEXPECT_FAIL("", "PersistentRangeMoving needs to be fixed", Continue);
|
|
QCOMPARE(caseContext.itemData("bar", KDevelop::CodeCompletionModel::MatchQuality).toInt(), 10);
|
|
release(top);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testCaseContextDifferentScope()
|
|
{
|
|
QByteArray method = "struct testStruct { enum testEnum { foo, bar } e; }; void test(testStruct s) { switch (s.e) { } }";
|
|
TopDUContext* top = parse(method, DumpNone);
|
|
int ctxt = 2;
|
|
int sctxt = 1;
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
CompletionItemTester caseContext(top->childContexts()[ctxt]->childContexts()[sctxt], "case ");
|
|
QCOMPARE(caseContext.completionContext->parentContext()->accessType(), Cpp::CodeCompletionContext::CaseAccess);
|
|
QEXPECT_FAIL("", "PersistentRangeMoving needs to be fixed", Continue);
|
|
QCOMPARE(caseContext.names, QStringList() << "testStruct" << "testStruct::foo" << "testStruct::bar");
|
|
release(top);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testCaseContextConstants()
|
|
{
|
|
QByteArray method = "enum testEnum { foo, bar };"
|
|
"testEnum enum_nc; const testEnum enum_c; const testEnum enum_cc = foo;"
|
|
"int int_nc; const int int_c; const int int_cc = 0;"
|
|
"float float_nc; const float float_c; const float float_cc = 0.0;"
|
|
"testEnum func_enum(); constexpr testEnum func_enum_cc();"
|
|
"int func_int(); constexpr int func_int_cc();"
|
|
"void func_void(); float func_float(); constexpr float func_float_cc();"
|
|
"void testcase() { switch (enum_nc) { } }";
|
|
TopDUContext* top = parse(method, DumpNone);
|
|
int sctxt = 1;
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
CompletionItemTester caseContext(top->childContexts().last()->childContexts()[sctxt], "case ");
|
|
QStringList names = caseContext.names;
|
|
QVERIFY(names.contains("testEnum"));
|
|
|
|
QVERIFY(!names.contains("enum_nc"));
|
|
QVERIFY(!names.contains("enum_c"));
|
|
QVERIFY( names.contains("enum_cc"));
|
|
|
|
QVERIFY(!names.contains("int_nc"));
|
|
QVERIFY(!names.contains("int_c"));
|
|
QVERIFY( names.contains("int_cc"));
|
|
|
|
QVERIFY(!names.contains("float_nc"));
|
|
QVERIFY(!names.contains("float_c"));
|
|
QVERIFY(!names.contains("float_cc"));
|
|
|
|
QVERIFY(!names.contains("func_void"));
|
|
QVERIFY(!names.contains("func_float"));
|
|
QVERIFY(!names.contains("func_float_cc"));
|
|
|
|
QEXPECT_FAIL("", "constexpr needs to be handled", Continue);
|
|
QVERIFY(!names.contains("func_enum"));
|
|
QVERIFY(names.contains("func_enum_cc"));
|
|
|
|
QEXPECT_FAIL("", "constexpr needs to be handled", Continue);
|
|
QVERIFY(!names.contains("func_int"));
|
|
QVERIFY(names.contains("func_int_cc"));
|
|
|
|
release (top);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testUnaryOperators()
|
|
{
|
|
QByteArray method = "class X { X(); int a; int b;}; int x,*z; X::X() { }";
|
|
TopDUContext* top = parse(method, DumpAll);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
int ctxt = 2;
|
|
CompletionItemTester plusplus(top->childContexts()[ctxt], "x *= ++");
|
|
QCOMPARE(plusplus.names, QStringList() << "int x *=" << "X" << "a" << "b" << "x" << "z" << "this");
|
|
CompletionItemTester plusplus2(top->childContexts()[ctxt], "x *= *z + ++");
|
|
QCOMPARE(plusplus2.names, QStringList() << "int* z +" << "X" << "a" << "b" << "x" << "z" << "this");
|
|
CompletionItemTester plusplus3(top->childContexts()[ctxt], "a + b = - ++");
|
|
QCOMPARE(plusplus3.names, QStringList() << "int b =" << "X" << "a" << "b" << "x" << "z" << "this");
|
|
CompletionItemTester plusplus4(top->childContexts()[ctxt], "x *= &*++");
|
|
QCOMPARE(plusplus4.names, QStringList() << "int x *=" << "X" << "a" << "b" << "x" << "z" << "this");
|
|
CompletionItemTester minusminus(top->childContexts()[ctxt], "x ~= --");
|
|
QCOMPARE(minusminus.names, QStringList() << "int x ~=" << "X" << "a" << "b" << "x" << "z" << "this");
|
|
CompletionItemTester minusminus2(top->childContexts()[ctxt], "x + *z + --");
|
|
QCOMPARE(minusminus2.names, QStringList() << "int* z +" << "X" << "a" << "b" << "x" << "z" << "this");
|
|
//TODO: should get "int b -" hint, but binary operators don't work behind paren
|
|
CompletionItemTester minusminus3(top->childContexts()[ctxt], "a + b -(----");
|
|
QCOMPARE(minusminus3.names, QStringList() << "X" << "a" << "b" << "x" << "z" << "this");
|
|
CompletionItemTester minusminus4(top->childContexts()[ctxt], "x ^= ++--");
|
|
QCOMPARE(minusminus4.names, QStringList() << "int x ^=" << "X" << "a" << "b" << "x" << "z" << "this");
|
|
CompletionItemTester notop(top->childContexts()[ctxt], "!++");
|
|
QCOMPARE(notop.names, QStringList() << "X" << "a" << "b" << "x" << "z" << "this");
|
|
CompletionItemTester notop2(top->childContexts()[ctxt], "!a & !");
|
|
QCOMPARE(notop2.names, QStringList() << "int a &" << "X" << "a" << "b" << "x" << "z" << "this");
|
|
CompletionItemTester notop3(top->childContexts()[ctxt], "b != !");
|
|
QCOMPARE(notop3.names, QStringList() << "int b !=" << "X" << "a" << "b" << "x" << "z" << "this");
|
|
CompletionItemTester notop4(top->childContexts()[ctxt], "x *= ~!");
|
|
QCOMPARE(notop4.names, QStringList() << "int x *=" << "X" << "a" << "b" << "x" << "z" << "this");
|
|
CompletionItemTester bitnot(top->childContexts()[ctxt], "~--");
|
|
QCOMPARE(bitnot.names, QStringList() << "X" << "a" << "b" << "x" << "z" << "this");
|
|
CompletionItemTester bitnot2(top->childContexts()[ctxt], "++~");
|
|
QCOMPARE(bitnot2.names, QStringList() << "X" << "a" << "b" << "x" << "z" << "this");
|
|
CompletionItemTester bitnot3(top->childContexts()[ctxt], "b ~= ~");
|
|
QCOMPARE(bitnot3.names, QStringList() << "int b ~=" << "X" << "a" << "b" << "x" << "z" << "this");
|
|
CompletionItemTester bitnot4(top->childContexts()[ctxt], "a * ~~~");
|
|
QCOMPARE(bitnot4.names, QStringList() << "int a *" << "X" << "a" << "b" << "x" << "z" << "this");
|
|
CompletionItemTester plus(top->childContexts()[ctxt], "*z = +");
|
|
QCOMPARE(plus.names, QStringList() << "int* z =" << "X" << "a" << "b" << "x" << "z" << "this");
|
|
//TODO: same as minusminus3
|
|
CompletionItemTester plus2(top->childContexts()[ctxt], "a + b +(+");
|
|
QCOMPARE(plus2.names, QStringList() << "X" << "a" << "b" << "x" << "z" << "this");
|
|
CompletionItemTester plus3(top->childContexts()[ctxt], "b ~= +b + -+");
|
|
QCOMPARE(bitnot3.names, QStringList() << "int b ~=" << "X" << "a" << "b" << "x" << "z" << "this");
|
|
CompletionItemTester plus4(top->childContexts()[ctxt], "a * +");
|
|
QCOMPARE(plus4.names, QStringList() << "int a *" << "X" << "a" << "b" << "x" << "z" << "this");
|
|
//TODO: same as minusminus3
|
|
CompletionItemTester minus(top->childContexts()[ctxt], "*z -(-");
|
|
QCOMPARE(minus.names, QStringList() << "X" << "a" << "b" << "x" << "z" << "this");
|
|
CompletionItemTester minus2(top->childContexts()[ctxt], "a ^ -(-");
|
|
QCOMPARE(minus2.names, QStringList() << "int a ^" << "X" << "a" << "b" << "x" << "z" << "this");
|
|
CompletionItemTester minus3(top->childContexts()[ctxt], "b ~= -");
|
|
QCOMPARE(minus3.names, QStringList() << "int b ~=" << "X" << "a" << "b" << "x" << "z" << "this");
|
|
CompletionItemTester minus4(top->childContexts()[ctxt], "a * +-");
|
|
QCOMPARE(minus4.names, QStringList() << "int a *" << "X" << "a" << "b" << "x" << "z" << "this");
|
|
CompletionItemTester refderef1(top->childContexts()[ctxt], "**&(&");
|
|
QCOMPARE(refderef1.names, QStringList() << "X" << "a" << "b" << "x" << "z" << "this");
|
|
//TODO: same as minusminus3
|
|
CompletionItemTester refderef2(top->childContexts()[ctxt], "a & b & (&");
|
|
QCOMPARE(refderef2.names, QStringList() << "X" << "a" << "b" << "x" << "z" << "this");
|
|
CompletionItemTester refderef3(top->childContexts()[ctxt], "a * x * *z * *");
|
|
QCOMPARE(refderef3.names, QStringList() << "int* z *" << "X" << "a" << "b" << "x" << "z" << "this");
|
|
CompletionItemTester refderef4(top->childContexts()[ctxt], "b & b & *");
|
|
QCOMPARE(refderef4.names, QStringList() << "int b &" << "X" << "a" << "b" << "x" << "z" << "this");
|
|
release(top);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testBinaryOperators()
|
|
{
|
|
QByteArray method = "class X { X(); int a; int b;}; X::X() { }";
|
|
TopDUContext* top = parse(method, DumpAll);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
CompletionItemTester gt(top->childContexts()[2], "if (a > ");
|
|
QCOMPARE(gt.names, QStringList() << "int a >" << "X" << "a" << "b" << "this");
|
|
CompletionItemTester lt(top->childContexts()[2], "if (a < ");
|
|
QCOMPARE(lt.names, QStringList() << "int a <" << "X" << "a" << "b" << "this");
|
|
CompletionItemTester plus(top->childContexts()[2], "if (a + ");
|
|
QCOMPARE(plus.names, QStringList() << "int a +" << "X" << "a" << "b" << "this");
|
|
CompletionItemTester minus(top->childContexts()[2], "if (a - ");
|
|
QCOMPARE(minus.names, QStringList() << "int a -" << "X" << "a" << "b" << "this");
|
|
CompletionItemTester multiply(top->childContexts()[2], "if (a * ");
|
|
QCOMPARE(multiply.names, QStringList() << "int a *" << "X" << "a" << "b" << "this");
|
|
CompletionItemTester divide(top->childContexts()[2], "if (a / ");
|
|
QCOMPARE(divide.names, QStringList() << "int a /" << "X" << "a" << "b" << "this");
|
|
CompletionItemTester modulus(top->childContexts()[2], "if (a % ");
|
|
QCOMPARE(modulus.names, QStringList() << "int a %" << "X" << "a" << "b" << "this");
|
|
CompletionItemTester xorop(top->childContexts()[2], "if (a ^ ");
|
|
QCOMPARE(xorop.names, QStringList() << "int a ^" << "X" << "a" << "b" << "this");
|
|
CompletionItemTester bitandop(top->childContexts()[2], "if (a & ");
|
|
QCOMPARE(bitandop.names, QStringList() << "int a &" << "X" << "a" << "b" << "this");
|
|
CompletionItemTester bitorop(top->childContexts()[2], "if (a | ");
|
|
QCOMPARE(bitorop.names, QStringList() << "int a |" << "X" << "a" << "b" << "this");
|
|
CompletionItemTester notequal(top->childContexts()[2], "if (a != ");
|
|
QCOMPARE(notequal.names, QStringList() << "int a !=" << "X" << "a" << "b" << "this");
|
|
CompletionItemTester ltequal(top->childContexts()[2], "if (a <= ");
|
|
QCOMPARE(ltequal.names, QStringList() << "int a <=" << "X" << "a" << "b" << "this");
|
|
CompletionItemTester gtequal(top->childContexts()[2], "if (a >= ");
|
|
QCOMPARE(gtequal.names, QStringList() << "int a >=" << "X" << "a" << "b" << "this");
|
|
CompletionItemTester plusequal(top->childContexts()[2], "if (a += ");
|
|
QCOMPARE(plusequal.names, QStringList() << "int a +=" << "X" << "a" << "b" << "this");
|
|
CompletionItemTester minusequal(top->childContexts()[2], "if (a -= ");
|
|
QCOMPARE(minusequal.names, QStringList() << "int a -=" << "X" << "a" << "b" << "this");
|
|
CompletionItemTester multiplyequal(top->childContexts()[2], "if (a *= ");
|
|
QCOMPARE(multiplyequal.names, QStringList() << "int a *=" << "X" << "a" << "b" << "this");
|
|
CompletionItemTester divideequal(top->childContexts()[2], "if (a /= ");
|
|
QCOMPARE(divideequal.names, QStringList() << "int a /=" << "X" << "a" << "b" << "this");
|
|
CompletionItemTester modulusequal(top->childContexts()[2], "if (a %= ");
|
|
QCOMPARE(modulusequal.names, QStringList() << "int a %=" << "X" << "a" << "b" << "this");
|
|
CompletionItemTester xorequal(top->childContexts()[2], "if (a ^= ");
|
|
QCOMPARE(xorequal.names, QStringList() << "int a ^=" << "X" << "a" << "b" << "this");
|
|
CompletionItemTester bitandequal(top->childContexts()[2], "if (a &= ");
|
|
QCOMPARE(bitandequal.names, QStringList() << "int a &=" << "X" << "a" << "b" << "this");
|
|
CompletionItemTester bitorequal(top->childContexts()[2], "if (a |= ");
|
|
QCOMPARE(bitorequal.names, QStringList() << "int a |=" << "X" << "a" << "b" << "this");
|
|
CompletionItemTester bitnotequal(top->childContexts()[2], "if (a ~= ");
|
|
QCOMPARE(bitnotequal.names, QStringList() << "int a ~=" << "X" << "a" << "b" << "this");
|
|
CompletionItemTester leftshift(top->childContexts()[2], "if (a << ");
|
|
QCOMPARE(leftshift.names, QStringList() << "int a <<" << "X" << "a" << "b" << "this");
|
|
CompletionItemTester rightshift(top->childContexts()[2], "if (a >> ");
|
|
QCOMPARE(rightshift.names, QStringList() << "int a >>" << "X" << "a" << "b" << "this");
|
|
CompletionItemTester leftshifteq(top->childContexts()[2], "if (a <<= ");
|
|
QCOMPARE(leftshifteq.names, QStringList() << "int a <<=" << "X" << "a" << "b" << "this");
|
|
CompletionItemTester rightshifteq(top->childContexts()[2], "if (a >>= ");
|
|
QCOMPARE(rightshifteq.names, QStringList() << "int a >>=" << "X" << "a" << "b" << "this");
|
|
CompletionItemTester equal(top->childContexts()[2], "if (a == ");
|
|
QCOMPARE(equal.names, QStringList() << "int a ==" << "X" << "a" << "b" << "this");
|
|
CompletionItemTester andop(top->childContexts()[2], "if (a && ");
|
|
QCOMPARE(andop.names, QStringList() << "int a &&" << "X" << "a" << "b" << "this");
|
|
CompletionItemTester orop(top->childContexts()[2], "if (a || ");
|
|
QCOMPARE(orop.names, QStringList() << "int a ||" << "X" << "a" << "b" << "this");
|
|
CompletionItemTester bracket(top->childContexts()[2], "if (a[ ");
|
|
QCOMPARE(bracket.names, QStringList() << "int a []" << "X" << "a" << "b" << "this");
|
|
CompletionItemTester assign(top->childContexts()[2], "if (a = ");
|
|
QCOMPARE(assign.names, QStringList() << "int a =" << "X" << "a" << "b" << "this");
|
|
CompletionItemTester assign2(top->childContexts()[2], "X z = ");
|
|
QCOMPARE(assign2.names, QStringList() << "class X =" << "X" << "a" << "b" << "this");
|
|
CompletionItemTester assign3(top->childContexts()[2], "X *z = new");
|
|
QCOMPARE(assign3.names, QStringList() << "class X =" << "X");
|
|
release(top);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testDeclarationIsInitialization()
|
|
{
|
|
QByteArray method = "template<class T1, class T2> class Templ { T1 ta(); T2 tb(); }; class X { X(); int a; int b;}; X::X() { }";
|
|
TopDUContext* top = parse(method, DumpAll);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
CompletionItemTester notActuallyADecl(top->childContexts()[4], "int g; if (a > this->");
|
|
QCOMPARE(notActuallyADecl.names, QStringList() << "int a >" << "a" << "b");
|
|
CompletionItemTester notActuallyADecl2(top->childContexts()[4], "int g; if (a < a > this->");
|
|
QCOMPARE(notActuallyADecl2.names, QStringList() << "int a >" << "a" << "b");
|
|
CompletionItemTester notTemplateEqOp(top->childContexts()[4], "int g = 1; int h =");
|
|
QCOMPARE(notTemplateEqOp.names, QStringList() << "int =" << "X" << "a" << "b" << "Templ" << "this");
|
|
CompletionItemTester isTemplateEqOp(top->childContexts()[4], "int g = 1; Templ<int,int> h =");
|
|
QCOMPARE(isTemplateEqOp.names, QStringList() << "class Templ< int, int > =" << "X" << "a" << "b" << "Templ" << "this");
|
|
CompletionItemTester isTemplate(top->childContexts()[4], "int g = 1; Templ<int,int> h(");
|
|
QCOMPARE(isTemplate.names, QStringList() << "Templ< int, int >(" << "X" << "a" << "b" << "Templ" << "this");
|
|
CompletionItemTester isTemplate2(top->childContexts()[4], "Templ<int,int> h(");
|
|
QCOMPARE(isTemplate2.names, isTemplate.names);
|
|
CompletionItemTester isTemplate3(top->childContexts()[4], "int g; Templ<int,int> h(");
|
|
QCOMPARE(isTemplate2.names, isTemplate.names);
|
|
CompletionItemTester constructInit(top->childContexts()[4], "int g = 1; X myx(");
|
|
QCOMPARE(constructInit.names, QStringList() << "X" << "X(" << "X" << "a" << "b" << "Templ" << "this");
|
|
CompletionItemTester ptr(top->childContexts()[4], "int g = 1; int* test =");
|
|
QCOMPARE(ptr.names, QStringList() << "int* =" << "X" << "a" << "b" << "Templ" << "this");
|
|
CompletionItemTester ptr2(top->childContexts()[4], "int g = 1; int** test =");
|
|
QCOMPARE(ptr2.names, QStringList() << "int** =" << "X" << "a" << "b" << "Templ" << "this");
|
|
release(top);
|
|
}
|
|
|
|
|
|
void TestCppCodeCompletion::testNoMemberAccess() {
|
|
QByteArray test = "class MyClass{ public:\n int myint; };\n\n";
|
|
|
|
TopDUContext* context = parse(test, DumpNone);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(context->childContexts().count(), 1);
|
|
|
|
CompletionItemTester testCase(context, "void "); //NoMemberAccess with non-empty valid-type expression
|
|
QVERIFY(testCase.completionContext->isValid());
|
|
QCOMPARE(testCase.names, QStringList()); //Valid, but should not offer any completions in this case
|
|
|
|
CompletionItemTester testCase1(context, "asdf "); //NoMemberAccess with non-empty invalid-type expression
|
|
QVERIFY(!testCase1.completionContext->isValid());
|
|
|
|
CompletionItemTester testCase2(context, " "); //NoMemberAccess with empty expression
|
|
QVERIFY(testCase2.completionContext->isValid());
|
|
QCOMPARE(testCase.names, QStringList()); //Theoretically should have "MyClass", but global completions aren't included
|
|
|
|
CompletionItemTester testCase3(context, "public: ");
|
|
QVERIFY(testCase2.completionContext->isValid());
|
|
|
|
CompletionItemTester testCase4(context, "protected: ");
|
|
QVERIFY(testCase2.completionContext->isValid());
|
|
|
|
CompletionItemTester testCase5(context, "private: ");
|
|
QVERIFY(testCase2.completionContext->isValid());
|
|
|
|
release(context);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testFunctionImplementation() {
|
|
//__hidden1 and _Hidden2 should not be visible in the code-completion, as their identifiers are reserved to C++ implementations and standard libraries.
|
|
addInclude("myclass.h", "namespace mynamespace { class myclass { void students(); }; }; class __hidden1; int _Hidden2; ");
|
|
QByteArray test = "#include \"myclass.h\"\nnamespace mynamespace { }";
|
|
|
|
TopDUContext* context = parse(test, DumpNone);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(context->childContexts().count(), 1);
|
|
|
|
CompletionItemTester testCase(context->childContexts()[0]);
|
|
QVERIFY(testCase.completionContext->isValid());
|
|
QCOMPARE(testCase.names, QStringList() << "mynamespace" << "myclass");
|
|
|
|
//TODO: If it ever becomes possible to test implementationhelpers, here it should be done
|
|
release(context);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testAliasDeclarationAccessPolicy() {
|
|
QByteArray test = "namespace Base { int One; int Two; int Three };\
|
|
class List { public: using Base::One; protected: using Base::Two; private: using Base::Three; }; int main(List a) {}";
|
|
|
|
TopDUContext* context = parse(test, DumpNone);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(context->childContexts().count(), 4);
|
|
|
|
CompletionItemTester testCase(context->childContexts()[3], "a.");
|
|
QVERIFY(testCase.completionContext->isValid());
|
|
QCOMPARE(testCase.names, QStringList() << "One");
|
|
|
|
AliasDeclaration* aliasDeclOne = dynamic_cast<AliasDeclaration*>(context->childContexts()[1]->localDeclarations()[0]);
|
|
AliasDeclaration* aliasDeclTwo = dynamic_cast<AliasDeclaration*>(context->childContexts()[1]->localDeclarations()[1]);
|
|
AliasDeclaration* aliasDeclThree = dynamic_cast<AliasDeclaration*>(context->childContexts()[1]->localDeclarations()[2]);
|
|
QVERIFY(aliasDeclOne && aliasDeclTwo && aliasDeclThree);
|
|
QVERIFY(aliasDeclOne->accessPolicy() == KDevelop::Declaration::Public);
|
|
QVERIFY(aliasDeclTwo->accessPolicy() == KDevelop::Declaration::Protected);
|
|
QVERIFY(aliasDeclThree->accessPolicy() == KDevelop::Declaration::Private);
|
|
|
|
release(context);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testKeywords() {
|
|
QByteArray test = "struct Values { int Value1; int Value2; struct Sub { int SubValue; }; }; Values v; int test(int a) {}";
|
|
|
|
TopDUContext* context = parse(test, DumpNone);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(context->childContexts().count(), 3);
|
|
|
|
int ctxt = 2;
|
|
CompletionItemTester constcast(context->childContexts()[ctxt], "Values* y = const_cast<");
|
|
QVERIFY(constcast.completionContext->isValid());
|
|
QCOMPARE(constcast.names, QStringList() << "struct Values =" << "Values");
|
|
CompletionItemTester staticcast(context->childContexts()[ctxt], "Values* y = static_cast<");
|
|
QVERIFY(staticcast.completionContext->isValid());
|
|
QCOMPARE(staticcast.names, QStringList() << "struct Values =" << "Values");
|
|
CompletionItemTester dynamiccast(context->childContexts()[ctxt], "Values* y = dynamic_cast<");
|
|
QVERIFY(dynamiccast.completionContext->isValid());
|
|
QCOMPARE(dynamiccast.names, QStringList() << "struct Values =" << "Values");
|
|
CompletionItemTester reinterpretcast(context->childContexts()[ctxt], "Values* y = reinterpret_cast<");
|
|
QVERIFY(reinterpretcast.completionContext->isValid());
|
|
QCOMPARE(reinterpretcast.names, QStringList() << "struct Values =" << "Values");
|
|
CompletionItemTester testConst(context->childContexts()[ctxt], "const ");
|
|
QVERIFY(testConst.completionContext->isValid());
|
|
QCOMPARE(testConst.names, QStringList() << "Values");
|
|
CompletionItemTester testTypedef(context->childContexts()[ctxt], "typedef ");
|
|
QVERIFY(testTypedef.completionContext->isValid());
|
|
QCOMPARE(testTypedef.names, QStringList() << "Values");
|
|
CompletionItemTester testPublic(context->childContexts()[ctxt], "public ");
|
|
QVERIFY(testPublic.completionContext->isValid());
|
|
QCOMPARE(testPublic.names, QStringList() << "Values");
|
|
CompletionItemTester testPublic2(context->childContexts()[0], "public: ");
|
|
QVERIFY(testPublic2.completionContext->isValid());
|
|
QCOMPARE(testPublic2.names, QStringList() << "Sub" << "Values");
|
|
CompletionItemTester testFakePublic(context->childContexts()[ctxt], "fakepublic ");
|
|
QVERIFY(!testFakePublic.completionContext->isValid());
|
|
CompletionItemTester testProtected(context->childContexts()[ctxt], "protected ");
|
|
QVERIFY(testProtected.completionContext->isValid());
|
|
QCOMPARE(testProtected.names, QStringList() << "Values");
|
|
CompletionItemTester testProtected2(context->childContexts()[0], "protected: ");
|
|
QVERIFY(testProtected2.completionContext->isValid());
|
|
QCOMPARE(testProtected2.names, QStringList() << "Sub" << "Values");
|
|
CompletionItemTester testFakeProtected(context->childContexts()[ctxt], "fakeprotected ");
|
|
QVERIFY(!testFakeProtected.completionContext->isValid());
|
|
CompletionItemTester testPrivate(context->childContexts()[ctxt], "private ");
|
|
QVERIFY(testPrivate.completionContext->isValid());
|
|
QCOMPARE(testPrivate.names, QStringList() << "Values");
|
|
CompletionItemTester testPrivate2(context->childContexts()[0], "private: ");
|
|
QVERIFY(testPrivate2.completionContext->isValid());
|
|
QCOMPARE(testPrivate2.names, QStringList() << "Sub" << "Values");
|
|
CompletionItemTester testVirtual(context->childContexts()[ctxt], "virtual ");
|
|
QVERIFY(testVirtual.completionContext->isValid());
|
|
QCOMPARE(testVirtual.names, QStringList() << "Values");
|
|
//TODO: fix ShowSignals, make this show only signals, and move emit tests to where it matters
|
|
CompletionItemTester testEmit(context->childContexts()[ctxt], "emit ");
|
|
QVERIFY(testEmit.completionContext->isValid());
|
|
QCOMPARE(testEmit.names, QStringList() << "a" << "Values" << "v" << "test");
|
|
CompletionItemTester testQEmit(context->childContexts()[ctxt], "Q_EMIT ");
|
|
QVERIFY(testQEmit.completionContext->isValid());
|
|
QCOMPARE(testQEmit.names, QStringList() << "a" << "Values" << "v" << "test");
|
|
CompletionItemTester testCase(context->childContexts()[ctxt], "switch (a) { case ");
|
|
QVERIFY(testCase.completionContext->isValid());
|
|
QCOMPARE(testCase.names, QStringList() << "Values" << "test");
|
|
CompletionItemTester testCase2(context->childContexts()[ctxt], "switch (a) { case v.");
|
|
QVERIFY(testCase2.completionContext->isValid());
|
|
QCOMPARE(testCase2.names, QStringList());
|
|
CompletionItemTester testReturn(context->childContexts()[ctxt], "return ");
|
|
QVERIFY(testReturn.completionContext->isValid());
|
|
QCOMPARE(testReturn.names, QStringList() << "return int" << "a" << "Values" << "v" << "test" << "v.Value1" << "v.Value2");
|
|
CompletionItemTester testReturn2(context->childContexts()[ctxt], "return v.");
|
|
QVERIFY(testReturn2.completionContext->isValid());
|
|
QCOMPARE(testReturn2.names, QStringList() << "return int" << "Value1" << "Value2");
|
|
CompletionItemTester testNew(context->childContexts()[ctxt], "Values* b = new ");
|
|
QVERIFY(testNew.completionContext->isValid());
|
|
QCOMPARE(testNew.names, QStringList() << "struct Values =" << "Values");
|
|
//TODO: make "new" onlyShow types in this case, and then skip it to create return context
|
|
CompletionItemTester testNew2(context->childContexts()[ctxt], "Values::Sub* b = new Values::");
|
|
QVERIFY(testNew2.completionContext->isValid());
|
|
QCOMPARE(testNew2.names, QStringList() << "Value1" << "Value2" << "Sub" );
|
|
CompletionItemTester testFakeNew(context->childContexts()[ctxt], "mynew ");
|
|
QVERIFY(!testFakeNew.completionContext->isValid());
|
|
CompletionItemTester testElse(context->childContexts()[ctxt], "if (a) {} else ");
|
|
QVERIFY(testElse.completionContext->isValid());
|
|
QCOMPARE(testElse.names, QStringList() << "a" << "Values" << "v" << "test");
|
|
CompletionItemTester testElse2(context->childContexts()[ctxt], "if (a) {} else Values::");
|
|
QVERIFY(testElse2.completionContext->isValid());
|
|
QCOMPARE(testElse2.names, QStringList() << "Value1" << "Value2" << "Sub" );
|
|
CompletionItemTester testThrow(context->childContexts()[ctxt], "throw ");
|
|
QVERIFY(testThrow.completionContext->isValid());
|
|
QCOMPARE(testThrow.names, QStringList() << "a" << "Values" << "v" << "test");
|
|
CompletionItemTester testThrow2(context->childContexts()[ctxt], "thow Values::");
|
|
QVERIFY(testThrow2.completionContext->isValid());
|
|
QCOMPARE(testThrow2.names, QStringList() << "Value1" << "Value2" << "Sub" );
|
|
CompletionItemTester testThrowNew(context->childContexts()[ctxt], "throw new");
|
|
QVERIFY(testThrowNew.completionContext->isValid());
|
|
QCOMPARE(testThrowNew.names, QStringList() << "Values");
|
|
//TODO: suboptimal results, see also testNew2
|
|
CompletionItemTester testThrowNew2(context->childContexts()[ctxt], "throw new Values::");
|
|
QVERIFY(testThrowNew2.completionContext->isValid());
|
|
QCOMPARE(testThrowNew2.names, QStringList() << "Value1" << "Value2" << "Sub");
|
|
CompletionItemTester testDelete(context->childContexts()[ctxt], "delete ");
|
|
QVERIFY(testDelete.completionContext->isValid());
|
|
QCOMPARE(testDelete.names, QStringList() << "a" << "Values" << "v" << "test");
|
|
CompletionItemTester testDelete2(context->childContexts()[ctxt], "delete Values::");
|
|
QVERIFY(testDelete2.completionContext->isValid());
|
|
QCOMPARE(testDelete2.names, QStringList() << "Value1" << "Value2" << "Sub" );
|
|
CompletionItemTester testDelete3(context->childContexts()[ctxt], "delete [ ] ");
|
|
QVERIFY(testDelete3.completionContext->isValid());
|
|
CompletionItemTester testFakeDelete(context->childContexts()[ctxt], "fakedelete ");
|
|
QVERIFY(!testFakeDelete.completionContext->isValid());
|
|
CompletionItemTester testFakeDelete2(context->childContexts()[ctxt], "fakedelete [] ");
|
|
QVERIFY(!testFakeDelete2.completionContext->isValid());
|
|
QCOMPARE(testDelete3.names, QStringList() << "a" << "Values" << "v" << "test");
|
|
release(context);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testArgumentMatching() {
|
|
{
|
|
QByteArray test = "struct A{ int m;}; void test(int q) { A a; }";
|
|
|
|
TopDUContext* context = parse( test, DumpNone /*DumpDUChain | DumpAST */);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(context->childContexts().count(), 3);
|
|
CompletionItemTester tester(context->childContexts()[2], "test(a.");
|
|
QVERIFY(tester.completionContext->parentContext());
|
|
QCOMPARE(tester.completionContext->parentContext()->knownArgumentTypes().count(), 0);
|
|
QCOMPARE(tester.completionContext->parentContext()->functionName(), QString("test"));
|
|
bool abort = false;
|
|
QCOMPARE(tester.completionContext->parentContext()->completionItems(abort).size(), 1);
|
|
release(context);
|
|
}
|
|
{
|
|
QByteArray test = "#define A(x) #x\n void test(char* a, char* b, int c) { } ";
|
|
|
|
TopDUContext* context = parse( test, DumpNone /*DumpDUChain | DumpAST */);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(context->childContexts().count(), 2);
|
|
CompletionItemTester tester(context->childContexts()[1], "test(\"hello\", A(a),");
|
|
QVERIFY(tester.completionContext->parentContext());
|
|
QCOMPARE(tester.completionContext->parentContext()->knownArgumentTypes().count(), 2);
|
|
QVERIFY(tester.completionContext->parentContext()->knownArgumentTypes()[0].type.abstractType());
|
|
QVERIFY(tester.completionContext->parentContext()->knownArgumentTypes()[1].type.abstractType());
|
|
QCOMPARE(tester.completionContext->parentContext()->knownArgumentTypes()[0].type.abstractType()->toString(), QString("const char*"));
|
|
QCOMPARE(tester.completionContext->parentContext()->knownArgumentTypes()[1].type.abstractType()->toString(), QString("const char*"));
|
|
release(context);
|
|
}
|
|
}
|
|
|
|
void TestCppCodeCompletion::testSubClassVisibility() {
|
|
{
|
|
QByteArray test = "typedef struct { int am; } A; void test() { A b; } ";
|
|
|
|
TopDUContext* context = parse( test, DumpAll /*DumpDUChain | DumpAST */);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(context->childContexts().count(), 3);
|
|
QCOMPARE(context->childContexts()[0]->localDeclarations().count(), 1);
|
|
QCOMPARE(context->childContexts()[0]->localDeclarations()[0]->kind(), Declaration::Instance);
|
|
QCOMPARE(CompletionItemTester(context->childContexts()[2], "b.").names, QStringList() << "am");
|
|
|
|
release(context);
|
|
}
|
|
{
|
|
QByteArray test = "struct A { int am; struct B { int bm; }; }; void test() { A::B b; } ";
|
|
|
|
TopDUContext* context = parse( test, DumpNone /*DumpDUChain | DumpAST */);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(context->childContexts().count(), 3);
|
|
QCOMPARE(CompletionItemTester(context->childContexts()[2], "b.").names, QStringList() << "bm");
|
|
|
|
release(context);
|
|
}
|
|
{
|
|
QByteArray test = "struct A { int am; struct B; }; struct A::B {int bm; }; void test() { A::B b; } ";
|
|
|
|
TopDUContext* context = parse( test, DumpNone /*DumpDUChain | DumpAST */);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(context->childContexts().count(), 4);
|
|
QCOMPARE(CompletionItemTester(context->childContexts()[3], "b.").names, QStringList() << "bm");
|
|
|
|
release(context);
|
|
}
|
|
}
|
|
|
|
void TestCppCodeCompletion::testMacrosInCodeCompletion()
|
|
{
|
|
QByteArray test = "#define test foo\n #define testfunction(X) x\n #define test2 fee\n struct A {int mem;}; void fun() { A foo; A* fee;\n }";
|
|
TopDUContext* context = parse( test, DumpNone /*DumpDUChain | DumpAST */);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
|
|
QCOMPARE(context->childContexts().size(), 3);
|
|
|
|
QCOMPARE(CompletionItemTester(context->childContexts()[2], "foo.", QString(), CursorInRevision(3, 0)).names, QStringList() << "mem");
|
|
QCOMPARE(CompletionItemTester(context->childContexts()[2], "test.", QString(), CursorInRevision(3, 0)).names, QStringList() << "mem");
|
|
|
|
QCOMPARE(CompletionItemTester(context->childContexts()[2], "fee->", QString(), CursorInRevision(3, 0)).names, QStringList() << "mem");
|
|
QCOMPARE(CompletionItemTester(context->childContexts()[2], "test2->", QString(), CursorInRevision(3, 0)).names, QStringList() << "mem");
|
|
QCOMPARE(CompletionItemTester(context->childContexts()[2], "testfunction2(", QString(), CursorInRevision(4, 0)).names.toSet(), QSet<QString>() << "testfunction2(" << "A" << "foo" << "fee");
|
|
QCOMPARE(CompletionItemTester(context->childContexts()[2], "testfunction(", QString(), CursorInRevision(4, 0)).names.toSet(), QSet<QString>() << "testfunction(" << "A" << "foo" << "fee");
|
|
|
|
release(context);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testConstructorCompletion() {
|
|
{
|
|
QByteArray test = "class A {}; class Class : public A { Class(); int m_1; float m_2; char m_3; static int m_4; }; ";
|
|
|
|
//74
|
|
TopDUContext* context = parse( test, DumpNone /*DumpDUChain | DumpAST */);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
|
|
{
|
|
kDebug() << "TEST 2";
|
|
CompletionItemTester tester(context, "class Class { Class(); int m_1; float m_2; char m_3; }; Class::Class(int m1, float m2, char m3) : m_1(1), m_2(m2), ");
|
|
|
|
//At first, only the members should be shown
|
|
kDebug() << tester.names;
|
|
QCOMPARE(tester.names, QStringList() << "m_3"); //m_1 should not be shown, because it is already initialized
|
|
///@todo Make sure that the item also inserts parens
|
|
}
|
|
|
|
{
|
|
CompletionItemTester tester(context, "Class::Class(int m1, float m2, char m3) : ");
|
|
|
|
//At first, only the members should be shown
|
|
kDebug() << tester.names;
|
|
QCOMPARE(tester.names, QStringList() << "A" << "m_1" << "m_2" << "m_3");
|
|
///@todo Make sure that the item also inserts parens
|
|
}
|
|
|
|
{
|
|
kDebug() << "TEST 3";
|
|
CompletionItemTester tester(context, "Class::Class(int m1, float m2, char m3) : m_1(");
|
|
|
|
//At first, only the members should be shown
|
|
QVERIFY(tester.names.size());
|
|
QVERIFY(tester.completionContext->parentContext()); //There must be a type-hinting context
|
|
QVERIFY(tester.completionContext->parentContext()->parentContext()); //There must be a type-hinting context
|
|
QVERIFY(!tester.completionContext->isConstructorInitialization());
|
|
QVERIFY(!tester.completionContext->parentContext()->isConstructorInitialization());
|
|
QVERIFY(tester.completionContext->parentContext()->accessType() == Cpp::CodeCompletionContext::FunctionCallAccess);
|
|
QVERIFY(tester.completionContext->parentContext()->parentContext()->isConstructorInitialization());
|
|
}
|
|
|
|
release(context);
|
|
}
|
|
}
|
|
|
|
void TestCppCodeCompletion::testConstructorUsageCompletion_data()
|
|
{
|
|
QTest::addColumn<QString>("code"); // existing source code
|
|
QTest::addColumn<QStringList>("args"); // completion arguments
|
|
QTest::addColumn<QStringList>("not_args"); // forbidden completion arguments
|
|
|
|
// only the default constructor is present
|
|
QTest::newRow("only default")
|
|
<< "class A { A(); };"
|
|
<< (QStringList() << "()")
|
|
<< QStringList();
|
|
// the default constructor and one overload
|
|
QTest::newRow("Default and int")
|
|
<< "class A { A(); A(int); };"
|
|
<< (QStringList() << "()" << "(int)")
|
|
<< QStringList();
|
|
// the default constructor and two overloads
|
|
QTest::newRow("Default, int and float")
|
|
<< "class A { A(); A(int); A(float); };"
|
|
<< (QStringList() << "()" << "(int)" << "(float)")
|
|
<< QStringList();
|
|
// deleted default constructor and two overloads
|
|
QTest::newRow("no default, int and float")
|
|
<< "class A { A() = delete; A(int); A(float); };"
|
|
<< (QStringList() << "(int)" << "(float)")
|
|
<< (QStringList() << "()");
|
|
// the default and copy constructors are present
|
|
QTest::newRow("default and copy")
|
|
<< "class A { A(); A(const A&); };"
|
|
<< (QStringList() << "()" << "(const A&)")
|
|
<< QStringList();
|
|
// the default and copy constructors are deleted
|
|
QTest::newRow("default and copy")
|
|
<< "class A { A() = delete; A(const A&) = delete; };"
|
|
<< QStringList()
|
|
<< (QStringList() << "()" << "(const A&)");
|
|
// the default and copy constructors are deleted, but another one is present
|
|
QTest::newRow("default and copy")
|
|
<< "class A { A() = delete; A(const A&) = delete; A(int); };"
|
|
<< (QStringList() << "(int)")
|
|
<< (QStringList() << "()" << "(const A&)");
|
|
}
|
|
|
|
void TestCppCodeCompletion::testConstructorUsageCompletion()
|
|
{
|
|
QFETCH(QString, code);
|
|
QFETCH(QStringList, args);
|
|
QFETCH(QStringList, not_args);
|
|
|
|
TopDUContext* context = parse( code.toAscii(), DumpNone /*DumpDUChain | DumpAST */);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
|
|
CompletionItemTester tester(context, "void k() { A *a = new A(");
|
|
|
|
QStringList foundargs;
|
|
for (int i = 0; i < tester.items.count(); i++)
|
|
foundargs << tester.itemData(i, KTextEditor::CodeCompletionModel::Arguments).toString();
|
|
qDebug() << foundargs << args << not_args;
|
|
foreach(const QString &arg, args)
|
|
QVERIFY(foundargs.contains(arg));
|
|
foreach(const QString &arg, not_args)
|
|
QVERIFY(!foundargs.contains(arg));
|
|
|
|
release(context);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testParentConstructor_data()
|
|
{
|
|
QTest::addColumn<QString>("code"); // existing source code
|
|
QTest::addColumn<QString>("completion"); // completion contents
|
|
|
|
// Parent has no constructor
|
|
QTest::newRow("NoParentConstructor") << "class A {}; class B : public A { B (int i); };" << "B::B(int i) { }";
|
|
// Argument name mismatch - choose it anyway
|
|
QTest::newRow("ArgNameMismatch") << "class A { A(int j) {} }; class B : public A { B(int i); };" << "B::B(int i): A(i) { }";
|
|
// Between two arguments with matching type, prefer one with matching name
|
|
QTest::newRow("PreferNameMatch") << "class A { A(int i, int j) {} }; class B : public A { B(int j, int i); };" << "B::B(int j, int i): A(i, j) { }";
|
|
// Argument type mismatch - do not choose anything for this argument
|
|
QTest::newRow("ArgTypeMismatch") << "class A { A(char i) {} }; class B : public A { B(int i); };" << "B::B(int i): A() { }";
|
|
// Some arguments match, others do not - leave the latter blank
|
|
QTest::newRow("SomeArgumentsMatch") << "class A { A(int i, char j) {} }; class B : public A { B(int i, int j); };" << "B::B(int i, int j): A(i, ) { }";
|
|
// Parent class has multiple constructors - choose the best matching
|
|
QTest::newRow("MultipleConstructors") << "class A { A(char c) {} A(int i, int j) {} }; class B : public A { B(int i); };" << "B::B(int i): A(i, i) { }";
|
|
QTest::newRow("MultipleConstructorsNoFullMatch") << "class A { A(char c, long l) {} A(int i, long l) {} }; class B : public A { B(int i); };" << "B::B(int i): A(i, ) { }";
|
|
// Omit call to default constructor if it is the best match
|
|
QTest::newRow("BestMatchDefault") << "class A { A() {} A(char c) {} }; class B : public A { B(int i); };" << "B::B(int i) { }";
|
|
|
|
// Multiple parents, each has its own matches
|
|
QTest::newRow("MultipleParents") << "class A { A(int i, char c) {} }; class B { B(char d, long l) {} }; class C : public A, public B { C(int i, long l, char c); };"
|
|
<< "C::C(int i, long int l, char c): A(i, c), B(c, l) { }";
|
|
// Last check
|
|
QTest::newRow("ComplexCase") << "class A { A(int i, double d, char c) {} A(int i, double d, long l) {} };"
|
|
"class B { B(double d, int j) {} B(char c) {} };"
|
|
"class C { };"
|
|
"class D : public A, public B, public C { D(short a1, double a2, long a3, int a4); };" <<
|
|
"D::D(short int a1, double a2, long int a3, int a4): A(a4, a2, a3), B(a2, a4) { }";
|
|
}
|
|
|
|
void TestCppCodeCompletion::testParentConstructor()
|
|
{
|
|
QFETCH(QString, code);
|
|
QFETCH(QString, completion);
|
|
TopDUContext* context = parse(code.toAscii(), DumpNone);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
CompletionItemTester tester(context, "void"); // Force a function context
|
|
Cpp::ImplementationHelperItem* constructorItem = dynamic_cast<Cpp::ImplementationHelperItem*>(tester.items[0].data());
|
|
QCOMPARE(constructorItem->insertionText().simplified(), completion.simplified());
|
|
release(context);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testOverride_data()
|
|
{
|
|
QTest::addColumn<QString>("parentCode"); // code in parent class
|
|
QTest::addColumn<QString>("prefix"); // completion prefix
|
|
QTest::addColumn<QString>("completion"); // expected completion contents
|
|
|
|
QTest::newRow("empty-prefix") << "virtual void foo();" << "" << "virtual void foo();";
|
|
QTest::newRow("public") << "virtual void foo();" << "public: " << "virtual void foo();";
|
|
QTest::newRow("protected") << "virtual void foo();" << "protected: " << "virtual void foo();";
|
|
QTest::newRow("private") << "virtual void foo();" << "private: " << "virtual void foo();";
|
|
}
|
|
|
|
void TestCppCodeCompletion::testOverride()
|
|
{
|
|
QFETCH(QString, parentCode);
|
|
QFETCH(QString, prefix);
|
|
QFETCH(QString, completion);
|
|
QString tmpl = QString(" class A {public: %1 };\n"
|
|
" class B : public A { };").arg(parentCode);
|
|
TopDUContext* context = parse(tmpl.toLocal8Bit(), DumpNone);
|
|
DUChainWriteLocker lock;
|
|
DUContext* BCtx = context->childContexts().last();
|
|
QCOMPARE(BCtx->localScopeIdentifier().toString(), QLatin1String("B"));
|
|
CompletionItemTester tester(BCtx, prefix);
|
|
bool found = false;
|
|
foreach(CompletionItemTester::Item item, tester.items) {
|
|
Cpp::ImplementationHelperItem* override = dynamic_cast<Cpp::ImplementationHelperItem*>(item.data());
|
|
if (!override) {
|
|
continue;
|
|
}
|
|
QCOMPARE(override->m_type, Cpp::ImplementationHelperItem::Override);
|
|
QCOMPARE(override->insertionText().simplified(), completion.simplified());
|
|
found = true;
|
|
break;
|
|
}
|
|
QVERIFY(found);
|
|
release(context);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testOverrideDeleted()
|
|
{
|
|
const QByteArray tmpl("class A {public: virtual void foo() = delete; };\n"
|
|
"class B : public A { };");
|
|
TopDUContext* context = parse(tmpl, DumpNone);
|
|
DUChainWriteLocker lock;
|
|
DUContext* BCtx = context->childContexts().last();
|
|
QCOMPARE(BCtx->localScopeIdentifier().toString(), QLatin1String("B"));
|
|
CompletionItemTester tester(BCtx, "");
|
|
bool found = false;
|
|
foreach(CompletionItemTester::Item item, tester.items) {
|
|
Cpp::ImplementationHelperItem* override = dynamic_cast<Cpp::ImplementationHelperItem*>(item.data());
|
|
if (!override) {
|
|
continue;
|
|
}
|
|
found = true;
|
|
break;
|
|
}
|
|
QVERIFY(!found);
|
|
release(context);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testSignalSlotCompletion() {
|
|
// By processing qobjectdefs.h, we make sure that the qt-specific macros are defined in the duchain through overriding (see setuphelpers.cpp)
|
|
addInclude("/qobjectdefs.h", "#define signals\n#define slots\n#define Q_SIGNALS\n#define Q_SLOTS\n#define Q_PRIVATE_SLOT\n#define SIGNAL\n#define SLOT\n int n;\n");
|
|
|
|
addInclude("QObject.h", "#include \"/qobjectdefs.h\"\n class QObject { void connect(QObject* from, const char* signal, QObject* to, const char* slot); void connect(QObject* from, const char* signal, const char* slot); };");
|
|
|
|
QByteArray test("#include \"QObject.h\"\n class TE; class A : public QObject { public slots: void slot1(); void slot2(TE*); signals: void signal1(TE*, char);void signal2(); public: void test() { } private: Q_PRIVATE_SLOT(d,void slot3(TE*)) };");
|
|
|
|
TopDUContext* context = parse( test, DumpAll );
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(context->childContexts().count(), 1);
|
|
QCOMPARE(context->childContexts()[0]->childContexts().count(), 8);
|
|
CompletionItemTester(context->childContexts()[0]->childContexts()[5], "connect( this, ");
|
|
QCOMPARE(CompletionItemTester(context->childContexts()[0]->childContexts()[5], "connect( this, ").names.toSet(), (QStringList() << "connect" << "signal1" << "signal2").toSet());
|
|
QCOMPARE(CompletionItemTester(context->childContexts()[0]->childContexts()[5], "connect( this, SIGNAL(").names.toSet(), (QStringList() << "connect" << "signal1" << "signal2").toSet());
|
|
kDebug() << "ITEMS:" << CompletionItemTester(context->childContexts()[0]->childContexts()[5], "connect( this, SIGNAL(signal2()), this, SLOT(").names;
|
|
QCOMPARE(CompletionItemTester(context->childContexts()[0]->childContexts()[5], "connect( this, SIGNAL(signal2()), this, ").names.toSet(), (QStringList() << "connect" << "signal1" << "signal2" << "slot1" << "slot2" << "slot3" << "Connect to A::signal2 ()").toSet());
|
|
QCOMPARE(CompletionItemTester(context->childContexts()[0]->childContexts()[5], "connect( this, SIGNAL(signal2()), this, SIGNAL(").names.toSet(), (QStringList() << "connect" << "signal1" << "signal2" << "Connect to A::signal2 ()").toSet());
|
|
QCOMPARE(CompletionItemTester(context->childContexts()[0]->childContexts()[5], "connect( this, SIGNAL(signal2()), this, SLOT(").names.toSet(), (QStringList() << "connect" << "slot1" << "slot2" << "slot3" << "Connect to A::signal2 ()" << "signal2").toSet());
|
|
QVERIFY(((QStringList() << "connect" << "signal1" << "signal2" << "slot1" << "slot2" << "slot3" << "Connect to A::signal2 ()").toSet() - CompletionItemTester(context->childContexts()[0]->childContexts()[5], "connect( this, SIGNAL(signal2()), ").names.toSet()).isEmpty());
|
|
QVERIFY(((QStringList() << "connect" << "signal1" << "signal2" << "Connect to A::signal2 ()").toSet() - CompletionItemTester(context->childContexts()[0]->childContexts()[5], "connect( this, SIGNAL(signal2()), SIGNAL(").names.toSet()).isEmpty());
|
|
QVERIFY(((QStringList() << "connect" << "slot1" << "slot2" << "slot3"<< "Connect to A::signal2 ()").toSet() - CompletionItemTester(context->childContexts()[0]->childContexts()[5], "connect( this, SIGNAL(signal2()), SLOT(").names.toSet()).isEmpty());
|
|
|
|
Declaration* decl = context->childContexts().last()->findDeclarations(Identifier("slot3")).first();
|
|
QVERIFY(decl);
|
|
QVERIFY(dynamic_cast<ClassFunctionDeclaration*>(decl));
|
|
QVERIFY(dynamic_cast<ClassFunctionDeclaration*>(decl)->accessPolicy() == Declaration::Private);
|
|
QVERIFY(dynamic_cast<ClassFunctionDeclaration*>(decl)->isSlot());
|
|
release(context);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testSignalSlotExecution()
|
|
{
|
|
// By processing qobjectdefs.h, we make sure that the qt-specific macros are defined in the duchain through overriding (see setuphelpers.cpp)
|
|
addInclude("/qobjectdefs.h", "#define signals\n#define slots\n#define Q_SIGNALS\n#define Q_SLOTS\n#define Q_PRIVATE_SLOT\n#define SIGNAL\n#define SLOT\n int n;\n");
|
|
addInclude("QObject.h", "#include \"/qobjectdefs.h\"\n class QObject { void connect(QObject* from, const char* signal, QObject* to, const char* slot); void connect(QObject* from, const char* signal, const char* slot); };");
|
|
|
|
QByteArray test("#include \"QObject.h\""
|
|
"\nclass A : public QObject { public slots: void slot1(); void slot2(void*);"
|
|
"signals: void signal1(void*, char); void signal2(void*); "
|
|
"\nvoid test() { connect( this, SIGNAL(signal2(void*)), SLOT() ); } };");
|
|
|
|
TopDUContext* top = parse( test, DumpNone );
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(top->childContexts().count(), 1);
|
|
QCOMPARE(top->childContexts()[0]->childContexts().count(), 6);
|
|
QVERIFY(top->problems().isEmpty());
|
|
|
|
DUContext* context = top->childContexts()[0]->childContexts()[5];
|
|
|
|
KTextEditor::Editor* editor = KTextEditor::EditorChooser::editor();
|
|
QVERIFY(editor);
|
|
KTextEditor::Document* doc = editor->createDocument(this);
|
|
QVERIFY(doc);
|
|
|
|
doc->setText(test);
|
|
KTextEditor::View* v = doc->createView(0);
|
|
|
|
// Test 1: SIGNAL(<here>signal2(void*)) parens balancing
|
|
{
|
|
doc->startEditing();
|
|
KTextEditor::Cursor c( 2, 36 );
|
|
v->setCursorPosition( c );
|
|
|
|
CompletionItemTester complCtx( context, "connect( this, SIGNAL(", "", CursorInRevision( c.line(), c.column() ) );
|
|
KSharedPtr<CompletionTreeItem> item;
|
|
for( int i = 0; i < complCtx.items.length(); ++i ) {
|
|
if( complCtx.itemData( i ).toString() == "signal1" ) {
|
|
item = complCtx.items.at( i );
|
|
}
|
|
}
|
|
QVERIFY( !item.isNull() );
|
|
|
|
item->execute( doc, Range( c, 0 ) );
|
|
QCOMPARE( doc->line( 2 ), QString("void test() { connect( this, SIGNAL(signal1(void*,char)), SLOT() ); } };") );
|
|
|
|
doc->endEditing();
|
|
}
|
|
|
|
// Test 2: SLOT(<here>) parens balancing
|
|
{
|
|
doc->startEditing();
|
|
KTextEditor::Cursor c( 2, 58 );
|
|
v->setCursorPosition( c );
|
|
|
|
CompletionItemTester complCtx( context, "connect( this, SIGNAL(signal1(void*,char)), SLOT(", "", CursorInRevision( c.line(), c.column() ) );
|
|
KSharedPtr<CompletionTreeItem> item;
|
|
for( int i = 0; i < complCtx.items.length(); ++i ) {
|
|
if( complCtx.itemData( i ).toString() == "slot2" ) {
|
|
item = complCtx.items.at( i );
|
|
}
|
|
}
|
|
QVERIFY( !item.isNull() );
|
|
|
|
item->execute( doc, Range( c, 0 ) );
|
|
QCOMPARE( doc->line( 2 ), QString("void test() { connect( this, SIGNAL(signal1(void*,char)), SLOT(slot2(void*)) ); } };") );
|
|
|
|
doc->endEditing();
|
|
}
|
|
// Test 3: Slot implementation helper: SLOT(<here>) parens balancing
|
|
{
|
|
doc->startEditing();
|
|
KTextEditor::Cursor c( 2, 58 );
|
|
v->setCursorPosition( c );
|
|
|
|
lock.unlock();
|
|
CompletionItemTester complCtx( context, "connect( this, SIGNAL(signal1(void*,char)), SLOT(", "slot3", CursorInRevision( c.line(), c.column() ) );
|
|
qDebug() << "TEST3 names: " << complCtx.names;
|
|
KSharedPtr<CompletionTreeItem> item;
|
|
for( int i = 0; i < complCtx.items.length(); ++i ) {
|
|
if( complCtx.itemData( i ).toString() == "slot3" ) {
|
|
item = complCtx.items.at( i );
|
|
}
|
|
}
|
|
QVERIFY( !item.isNull() );
|
|
|
|
item->execute( doc, Range( c, 0 ) );
|
|
lock.lock();
|
|
QEXPECT_FAIL("", "Slot is not replaced because the engine fails to create the declaration.", Continue);
|
|
QCOMPARE( doc->line( 2 ), QString("void test() { connect( this, SIGNAL(signal1(void*,char)), SLOT(slot3(void*)) ); } };") );
|
|
|
|
doc->endEditing();
|
|
}
|
|
|
|
release(top);
|
|
|
|
delete v;
|
|
delete doc;
|
|
}
|
|
|
|
void TestCppCodeCompletion::testAssistant() {
|
|
{
|
|
QByteArray test = "#define A hallo(3) = 1\n void test() { A; bla = 5; }";
|
|
|
|
TopDUContext* context = parse( test, DumpAll );
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(context->problems().count(), 1);
|
|
release(context);
|
|
}
|
|
{
|
|
QByteArray test = "int n; class C { C() : member(n) {} }; }";
|
|
|
|
TopDUContext* context = parse( test, DumpAll );
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(context->problems().count(), 1);
|
|
{
|
|
KSharedPtr<Cpp::MissingDeclarationProblem> mdp( dynamic_cast<Cpp::MissingDeclarationProblem*>(context->problems()[0].data()) );
|
|
QVERIFY(mdp);
|
|
kDebug() << "problem:" << mdp->description();
|
|
QCOMPARE(mdp->type->containerContext.data(), context->childContexts()[0]);
|
|
QCOMPARE(mdp->type->identifier().toString(), QString("member"));
|
|
QVERIFY(mdp->type->assigned.type.isValid());
|
|
QCOMPARE(TypeUtils::removeConstants(mdp->type->assigned.type.abstractType(), context)->toString(), QString("int"));
|
|
}
|
|
release(context);
|
|
}
|
|
{
|
|
QByteArray test = "class C {}; void test() {C c; c.value = 5; int i = c.value2; i = c.value3(); }";
|
|
|
|
TopDUContext* context = parse( test, DumpAll );
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(context->problems().count(), 3);
|
|
{
|
|
KSharedPtr<Cpp::MissingDeclarationProblem> mdp( dynamic_cast<Cpp::MissingDeclarationProblem*>(context->problems()[0].data()) );
|
|
QVERIFY(mdp);
|
|
kDebug() << "problem:" << mdp->description();
|
|
QCOMPARE(mdp->type->containerContext.data(), context->childContexts()[0]);
|
|
QCOMPARE(mdp->type->identifier().toString(), QString("value"));
|
|
QVERIFY(mdp->type->assigned.type.isValid());
|
|
QCOMPARE(TypeUtils::removeConstants(mdp->type->assigned.type.abstractType(), context)->toString(), QString("int"));
|
|
QCOMPARE(context->childContexts().count(), 3);
|
|
}
|
|
{
|
|
///@todo Make this work as well
|
|
/* KSharedPtr<Cpp::MissingDeclarationProblem> mdp( dynamic_cast<Cpp::MissingDeclarationProblem*>(context->problems()[1].data()) );
|
|
QVERIFY(mdp);
|
|
kDebug() << "problem:" << mdp->description();
|
|
QCOMPARE(mdp->type->containerContext.data(), context->childContexts()[0]);
|
|
QCOMPARE(mdp->type->identifier().toString(), QString("value2"));
|
|
QVERIFY(!mdp->type->assigned.type.isValid());
|
|
QVERIFY(mdp->type->convertedTo.type.isValid());
|
|
QCOMPARE(TypeUtils::removeConstants(mdp->type->convertedTo.type.abstractType())->toString(), QString("int"));
|
|
QCOMPARE(context->childContexts().count(), 3);*/
|
|
}
|
|
{
|
|
KSharedPtr<Cpp::MissingDeclarationProblem> mdp( dynamic_cast<Cpp::MissingDeclarationProblem*>(context->problems()[2].data()) );
|
|
QVERIFY(mdp);
|
|
kDebug() << "problem:" << mdp->description();
|
|
QCOMPARE(mdp->type->containerContext.data(), context->childContexts()[0]);
|
|
QCOMPARE(mdp->type->identifier().toString(), QString("value3"));
|
|
QVERIFY(!mdp->type->assigned.type.isValid());
|
|
QVERIFY(mdp->type->convertedTo.type.isValid());
|
|
QCOMPARE(TypeUtils::removeConstants(mdp->type->convertedTo.type.abstractType(), context)->toString(), QString("int"));
|
|
QCOMPARE(context->childContexts().count(), 3);
|
|
}
|
|
release(context);
|
|
}
|
|
{
|
|
QByteArray test = "class C {}; void test() {C c; int valueName; c.functionName(valueName); }";
|
|
|
|
TopDUContext* context = parse( test, DumpAll );
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(context->problems().count(), 1);
|
|
{
|
|
KSharedPtr<Cpp::MissingDeclarationProblem> mdp( dynamic_cast<Cpp::MissingDeclarationProblem*>(context->problems()[0].data()) );
|
|
QVERIFY(mdp);
|
|
kDebug() << "problem:" << mdp->description();
|
|
QCOMPARE(mdp->type->containerContext.data(), context->childContexts()[0]);
|
|
QCOMPARE(mdp->type->identifier().toString(), QString("functionName"));
|
|
QVERIFY(!mdp->type->assigned.type.isValid());
|
|
QCOMPARE(mdp->type->arguments.count(), 1);
|
|
///@todo Use the value-names of values given to the function
|
|
// QCOMPARE(mdp->type->arguments[0].second, QString("valueName"));
|
|
QCOMPARE(context->childContexts().count(), 3);
|
|
}
|
|
release(context);
|
|
}
|
|
}
|
|
|
|
void TestCppCodeCompletion::testImportTypedef() {
|
|
{
|
|
QByteArray test = "class Class { }; typedef Class Klass; class C : public Class { };";
|
|
|
|
TopDUContext* context = parse( test, DumpNone /*DumpDUChain | DumpAST */);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(context->childContexts().count(), 2);
|
|
QCOMPARE(context->childContexts()[1]->importedParentContexts().count(), 1);
|
|
QCOMPARE(context->childContexts()[1]->importedParentContexts()[0].context(context->topContext()), context->childContexts()[0]);
|
|
}
|
|
{
|
|
QByteArray test = "class A { public: int m; }; template<class Base> class C : public Base { };";
|
|
|
|
TopDUContext* context = parse( test, DumpNone /*DumpDUChain | DumpAST */);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
Declaration* CDecl = findDeclaration(context, QualifiedIdentifier("C<A>"));
|
|
QVERIFY(CDecl);
|
|
QVERIFY(CDecl->internalContext());
|
|
QVector<DUContext::Import> imports = CDecl->internalContext()->importedParentContexts();
|
|
QCOMPARE(imports.size(), 2);
|
|
QVERIFY(imports[0].context(context));
|
|
QVERIFY(imports[1].context(context));
|
|
QCOMPARE(imports[0].context(context)->type(), DUContext::Template);
|
|
QCOMPARE(imports[1].context(context)->type(), DUContext::Class);
|
|
QCOMPARE(imports[1].context(context)->scopeIdentifier(true), QualifiedIdentifier("A"));
|
|
}
|
|
{
|
|
QByteArray test = "class A { public: int m; }; template<class Base> class C : public Base { }; typedef C<A> TheBase; class B : public TheBase { }; class E : public B{ };";
|
|
|
|
TopDUContext* context = parse( test, DumpNone /*DumpDUChain | DumpAST */);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
Declaration* typeDef = findDeclaration(context, QualifiedIdentifier("TheBase"));
|
|
QVERIFY(typeDef);
|
|
QVERIFY(typeDef->isTypeAlias());
|
|
QVERIFY(typeDef->type<KDevelop::TypeAliasType>());
|
|
|
|
Declaration* BDecl = findDeclaration(context, QualifiedIdentifier("B"));
|
|
QVERIFY(BDecl);
|
|
QCOMPARE(BDecl->internalContext()->importedParentContexts().size(), 1);
|
|
QVERIFY(BDecl->internalContext()->importedParentContexts()[0].context(context));
|
|
}
|
|
|
|
}
|
|
|
|
void TestCppCodeCompletion::testPrivateVariableCompletion() {
|
|
TEST_FILE_PARSE_ONLY
|
|
QByteArray test = "class C {void test() {}; int i; };";
|
|
|
|
DUContext* context = parse( test, DumpAll /*DumpDUChain | DumpAST */);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
|
|
QVERIFY(context->type() != DUContext::Helper);
|
|
QCOMPARE(context->childContexts().count(), 1);
|
|
DUContext* CContext = context->childContexts()[0];
|
|
QCOMPARE(CContext->type(), DUContext::Class);
|
|
QCOMPARE(CContext->childContexts().count(), 2);
|
|
QCOMPARE(CContext->localDeclarations().count(), 2);
|
|
DUContext* testContext = CContext->childContexts()[1];
|
|
QCOMPARE(testContext->type(), DUContext::Other );
|
|
QVERIFY(testContext->owner());
|
|
QCOMPARE(testContext->localScopeIdentifier(), QualifiedIdentifier("test"));
|
|
lock.unlock();
|
|
|
|
CompletionItemTester tester(testContext);
|
|
kDebug() << "names:" << tester.names;
|
|
QCOMPARE(tester.names.toSet(), (QStringList() << "C" << "i" << "test" << "this").toSet());
|
|
|
|
lock.lock();
|
|
release(context);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testCompletionPrefix() {
|
|
TEST_FILE_PARSE_ONLY
|
|
{
|
|
QByteArray method("struct Test {int m;}; Test t;Test* t2;void test() {}");
|
|
TopDUContext* top = parse(method, DumpNone);
|
|
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(top->childContexts().size(), 3);
|
|
//Make sure the completion behind "for <" does not only show types
|
|
QVERIFY(CompletionItemTester(top->childContexts()[2], ";for(int a = 0; a < t2->").names.contains("m"));
|
|
QVERIFY(CompletionItemTester(top->childContexts()[2], ";for(int a = 0; a < ").names.contains("t2"));
|
|
//Make sure that only types are shown as template parameters
|
|
QVERIFY(!CompletionItemTester(top->childContexts()[2], "Test<").names.contains("t2"));
|
|
|
|
QCOMPARE(CompletionItemTester(top->childContexts()[2], "if((t).").names, QStringList() << "m");
|
|
QCOMPARE(CompletionItemTester(top->childContexts()[2], "Test t(&t2->").names, QStringList() << "Test(" << "m");
|
|
|
|
QCOMPARE(CompletionItemTester(top->childContexts()[2], "Test(\"(\").").names, QStringList() << "m");
|
|
|
|
QCOMPARE(CompletionItemTester(top->childContexts()[2], "Test(\" \\\" quotedText( \\\" \").").names, QStringList() << "m");
|
|
|
|
QVERIFY(CompletionItemTester(top->childContexts()[2], ";int i = ").completionContext->parentContext());
|
|
QVERIFY(CompletionItemTester(top->childContexts()[2], ";int i ( ").completionContext->parentContext());
|
|
bool abort = false;
|
|
QVERIFY(CompletionItemTester(top->childContexts()[2], ";int i = ").completionContext->parentContext()->completionItems(abort).size());
|
|
QVERIFY(CompletionItemTester(top->childContexts()[2], ";int i ( ").completionContext->parentContext()->completionItems(abort).size());
|
|
release(top);
|
|
}
|
|
}
|
|
|
|
void TestCppCodeCompletion::testStringProblem() {
|
|
TEST_FILE_PARSE_ONLY
|
|
{
|
|
QByteArray method("void test() {int i;};");
|
|
TopDUContext* top = parse(method, DumpNone);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(top->childContexts().count(), 2);
|
|
CompletionItemTester tester(top->childContexts()[1],QString("bla url('\\\"');"));
|
|
|
|
QCOMPARE(tester.names.toSet(), (QStringList() << "i" << "test").toSet());;
|
|
release(top);
|
|
}
|
|
{
|
|
QByteArray method("void test() {int i;};");
|
|
TopDUContext* top = parse(method, DumpNone);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(top->childContexts().count(), 2);
|
|
CompletionItemTester tester(top->childContexts()[1],QString("bla url(\"http://wwww.bla.de/\");"));
|
|
|
|
QCOMPARE(tester.names.toSet(), (QStringList() << "i" << "test").toSet());;
|
|
release(top);
|
|
}
|
|
}
|
|
|
|
void TestCppCodeCompletion::testInheritanceVisibility() {
|
|
TEST_FILE_PARSE_ONLY
|
|
QByteArray method("class A { public: class AMyClass {}; }; class B : protected A { public: class BMyClass {}; }; class C : private B{ public: class CMyClass {}; }; class D : public C { class DMyClass{}; }; ");
|
|
TopDUContext* top = parse(method, DumpNone);
|
|
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
|
|
QCOMPARE(top->childContexts().count(), 4);
|
|
|
|
QCOMPARE(top->childContexts()[1]->type(), DUContext::Class);
|
|
QVERIFY(top->childContexts()[1]->owner());
|
|
QVERIFY(Cpp::localClassFromCodeContext(top->childContexts()[1]));
|
|
//From within B, MyClass is visible, because of the protected inheritance
|
|
QCOMPARE(top->childContexts()[1]->localDeclarations().size(), 1);
|
|
QVERIFY(!Cpp::isAccessible(top, dynamic_cast<ClassMemberDeclaration*>(top->childContexts()[0]->localDeclarations()[0]), top, top->childContexts()[1]));
|
|
QCOMPARE(CompletionItemTester(top->childContexts()[1], "A::").names, QStringList() << "AMyClass");
|
|
QCOMPARE(CompletionItemTester(top->childContexts()[1]).names.toSet(), QSet<QString>() << "BMyClass" << "AMyClass" << "A" << "B" );
|
|
QCOMPARE(CompletionItemTester(top, "A::").names, QStringList() << "AMyClass");
|
|
kDebug() << "list:" << CompletionItemTester(top, "B::").names << CompletionItemTester(top, "A::").names.size();
|
|
QCOMPARE(CompletionItemTester(top, "B::").names, QStringList() << "BMyClass");
|
|
QCOMPARE(CompletionItemTester(top->childContexts()[2]).names.toSet(), QSet<QString>() << "CMyClass" << "BMyClass" << "AMyClass" << "C" << "B" << "A");
|
|
QCOMPARE(CompletionItemTester(top, "C::").names.toSet(), QSet<QString>() << "CMyClass");
|
|
QCOMPARE(CompletionItemTester(top->childContexts()[3]).names.toSet(), QSet<QString>() << "DMyClass" << "CMyClass" << "D" << "C" << "B" << "A");
|
|
QCOMPARE(CompletionItemTester(top, "D::").names.toSet(), QSet<QString>() << "CMyClass" ); //DMyClass is private
|
|
}
|
|
|
|
|
|
|
|
void TestCppCodeCompletion::testConstVisibility() {
|
|
TEST_FILE_PARSE_ONLY
|
|
QByteArray method("struct test { void f(); void e() const; }; int main() { const test a; } void test::e() const { }");
|
|
TopDUContext* top = parse(method, DumpNone);
|
|
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
|
|
QCOMPARE(top->childContexts().count(), 5);
|
|
|
|
kDebug() << "list:" << CompletionItemTester(top->childContexts()[2], "a.").names << CompletionItemTester(top->childContexts()[2], "a.").names.size();
|
|
QCOMPARE(CompletionItemTester(top->childContexts()[2], "a.").names.toSet(), QSet<QString>() << "e");
|
|
kDebug() << "list:" << CompletionItemTester(top->childContexts()[4], "").names << CompletionItemTester(top->childContexts()[4], "").names.size();
|
|
QCOMPARE(CompletionItemTester(top->childContexts()[4], "").names.toSet(), QSet<QString>() << "e" << "test" << "main" << "this");
|
|
}
|
|
|
|
void TestCppCodeCompletion::testConstOverloadVisibility()
|
|
{
|
|
// see also: https://bugs.kde.org/show_bug.cgi?id=267877
|
|
TEST_FILE_PARSE_ONLY
|
|
QByteArray method("struct test { test foo(); const test& foo() const; void bar() const; void asdf(); };\n"
|
|
"int main() { const test testConst; test testNonConst; }");
|
|
TopDUContext* top = parse(method, DumpNone);
|
|
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
|
|
QCOMPARE(top->childContexts().count(), 3);
|
|
QCOMPARE(top->childContexts().first()->localDeclarations().size(), 4);
|
|
|
|
CompletionItemTester tester = CompletionItemTester(top->childContexts()[2], "testConst.");
|
|
QCOMPARE(tester.names.size(), 2);
|
|
// non-const foo() should be hidden by overloaded const version
|
|
QVERIFY(!tester.containsDeclaration(
|
|
top->childContexts().first()->localDeclarations().at(0)
|
|
));
|
|
// const& foo() const
|
|
QVERIFY(tester.containsDeclaration(
|
|
top->childContexts().first()->localDeclarations().at(1)
|
|
));
|
|
// bar is always shown (not overloaded and const)
|
|
QVERIFY(tester.containsDeclaration(
|
|
top->childContexts().first()->localDeclarations().at(2)
|
|
));
|
|
// foo is hidden since it's non-const
|
|
QVERIFY(!tester.containsDeclaration(
|
|
top->childContexts().first()->localDeclarations().at(3)
|
|
));
|
|
|
|
tester = CompletionItemTester(top->childContexts()[2], "testNonConst.");
|
|
QCOMPARE(tester.names.size(), 3);
|
|
// non-const foo()
|
|
QVERIFY(tester.containsDeclaration(
|
|
top->childContexts().first()->localDeclarations().at(0)
|
|
));
|
|
// const& foo() const hidden by overloaded non-const foo
|
|
QVERIFY(!tester.containsDeclaration(
|
|
top->childContexts().first()->localDeclarations().at(1)
|
|
));
|
|
// bar is always shown (not overloaded and const)
|
|
QVERIFY(tester.containsDeclaration(
|
|
top->childContexts().first()->localDeclarations().at(2)
|
|
));
|
|
// foo is shown here as well
|
|
QVERIFY(tester.containsDeclaration(
|
|
top->childContexts().first()->localDeclarations().at(3)
|
|
));
|
|
}
|
|
|
|
void TestCppCodeCompletion::testFriendVisibility() {
|
|
TEST_FILE_PARSE_ONLY
|
|
QByteArray method("class A { class PrivateClass {}; friend class B; }; class B{};");
|
|
TopDUContext* top = parse(method, DumpNone);
|
|
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
|
|
QCOMPARE(top->childContexts().count(), 2);
|
|
|
|
//No type within A, so there should be no items
|
|
QCOMPARE(CompletionItemTester(top->childContexts()[1], "A::").names, QStringList() << "PrivateClass");
|
|
}
|
|
|
|
void TestCppCodeCompletion::testLocalUsingNamespace() {
|
|
TEST_FILE_PARSE_ONLY
|
|
{
|
|
QByteArray method("namespace Fuu { int test0(); }; namespace Foo { using namespace Fuu; int test() {} } void Bar() { using namespace Foo; int b = test(); }");
|
|
TopDUContext* top = parse(method, DumpAll);
|
|
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(top->childContexts().count(), 4);
|
|
QCOMPARE(top->childContexts()[1]->localDeclarations().size(), 2);
|
|
QCOMPARE(top->childContexts()[3]->localDeclarations().size(), 2);
|
|
QVERIFY(top->childContexts()[1]->localDeclarations()[1]->uses().size());
|
|
QVERIFY(top->childContexts()[3]->findLocalDeclarations(KDevelop::globalImportIdentifier(), KDevelop::CursorInRevision::invalid(), 0, KDevelop::AbstractType::Ptr(), KDevelop::DUContext::NoFiltering).size());
|
|
// QVERIFY(top->childContexts()[2]->findDeclarations(KDevelop::globalImportIdentifier).size());
|
|
|
|
QVERIFY(CompletionItemTester(top->childContexts()[3]).names.contains("test"));
|
|
QVERIFY(CompletionItemTester(top->childContexts()[3]).names.contains("test0"));
|
|
// QVERIFY(CompletionItemTester(top->childContexts()[3], "Foo::").names.contains("test0"));
|
|
release(top);
|
|
}
|
|
}
|
|
|
|
void TestCppCodeCompletion::testTemplateFunction() {
|
|
QByteArray method("template<class A> void test(A i); void t() { }");
|
|
TopDUContext* top = parse(method, DumpNone);
|
|
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
|
|
QCOMPARE(top->childContexts().count(), 4);
|
|
|
|
{
|
|
CompletionItemTester tester1(top->childContexts()[3], "test<");
|
|
QVERIFY(tester1.completionContext->parentContext());
|
|
CompletionItemTester tester2 = tester1.parent();
|
|
QCOMPARE(tester2.items.size(), 1);
|
|
Cpp::NormalDeclarationCompletionItem* item = dynamic_cast<Cpp::NormalDeclarationCompletionItem*>(tester2.items[0].data());
|
|
QVERIFY(item);
|
|
QVERIFY(item->completingTemplateParameters());
|
|
}
|
|
{
|
|
kDebug() << "second test";
|
|
CompletionItemTester tester1(top->childContexts()[3], "test<int>(");
|
|
QVERIFY(tester1.completionContext->parentContext());
|
|
CompletionItemTester tester2 = tester1.parent();
|
|
QCOMPARE(tester2.items.size(), 1);
|
|
Cpp::NormalDeclarationCompletionItem* item = dynamic_cast<Cpp::NormalDeclarationCompletionItem*>(tester2.items[0].data());
|
|
QVERIFY(item);
|
|
QVERIFY(!item->completingTemplateParameters());
|
|
QVERIFY(tester2.completionContext->matchTypes().size() == 1);
|
|
QVERIFY(tester2.completionContext->matchTypes()[0].type<IntegralType>());
|
|
}
|
|
|
|
release(top);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testTemplateArguments() {
|
|
QByteArray method("template<class T> struct I; typedef I<int> II; template<class T> struct Test { T t; }; ");
|
|
TopDUContext* top = parse(method, DumpNone);
|
|
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
|
|
QCOMPARE(top->childContexts().count(), 3);
|
|
|
|
QVERIFY(findDeclaration(top, QualifiedIdentifier("II")));
|
|
|
|
Declaration* decl = findDeclaration(top, QualifiedIdentifier("Test<II>::t"));
|
|
QVERIFY(decl);
|
|
QVERIFY(decl->abstractType());
|
|
QVERIFY(decl->type<TypeAliasType>());
|
|
|
|
//Since II is not template-dependent, the type should have stayed a TypeAliasType
|
|
QCOMPARE(Identifier(decl->abstractType()->toString()), Identifier("II"));
|
|
|
|
release(top);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testCompletionBehindTypedeffedConstructor() {
|
|
QByteArray method("template<class T> struct A { A(T); int m; }; typedef A<int> TInt; void test() {}");
|
|
TopDUContext* top = parse(method, DumpAll);
|
|
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(top->childContexts().count(), 4);
|
|
QCOMPARE(top->childContexts()[1]->localDeclarations().size(), 2);
|
|
|
|
//Member completion
|
|
// NOTE: constructor A is not listed, as you can't call the constructor in this way
|
|
QCOMPARE(CompletionItemTester(top->childContexts()[3], "A<int>().").names.toSet(), (QStringList() << QString("m")).toSet());
|
|
QCOMPARE(CompletionItemTester(top->childContexts()[3], "TInt().").names.toSet(), (QStringList() << QString("m")).toSet());
|
|
|
|
//Argument-hints
|
|
kDebug() << CompletionItemTester(top->childContexts()[3], "TInt(").parent().names;
|
|
QVERIFY(CompletionItemTester(top->childContexts()[3], "TInt(").parent().names.contains("A"));
|
|
QVERIFY(CompletionItemTester(top->childContexts()[3], "TInt ti(").parent().names.contains("A"));
|
|
|
|
release(top);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testCompletionInExternalClassDefinition() {
|
|
{
|
|
QByteArray method("class A { class Q; class B; }; class A::B {class C;}; class A::B::C{void test(); }; void A::B::test() {}; void A::B::C::test() {}");
|
|
TopDUContext* top = parse(method, DumpAll);
|
|
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(top->childContexts().count(), 7);
|
|
QCOMPARE(top->childContexts()[1]->childContexts().count(), 1);
|
|
QVERIFY(CompletionItemTester(top->childContexts()[1]->childContexts()[0]).names.contains("Q"));
|
|
QVERIFY(CompletionItemTester(top->childContexts()[2]->childContexts()[0]).names.contains("Q"));
|
|
QVERIFY(CompletionItemTester(top->childContexts()[3]).names.contains("Q"));
|
|
QVERIFY(CompletionItemTester(top->childContexts()[5]).names.contains("Q"));
|
|
release(top);
|
|
}
|
|
}
|
|
|
|
void TestCppCodeCompletion::testTemplateMemberAccess() {
|
|
{
|
|
QByteArray method("template<class T> struct I; template<class T> class Test { public: typedef I<T> It; }; template<class T> struct I { }; Test<int>::It test;");
|
|
TopDUContext* top = parse(method, DumpNone);
|
|
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
|
|
QCOMPARE(top->localDeclarations().count(), 4);
|
|
AbstractType::Ptr type = TypeUtils::unAliasedType(top->localDeclarations()[3]->abstractType());
|
|
QVERIFY(type);
|
|
IdentifiedType* identified = dynamic_cast<IdentifiedType*>(type.unsafeData());
|
|
QVERIFY(identified);
|
|
QVERIFY(!identified->declarationId().isDirect());
|
|
QString specializationString = IndexedInstantiationInformation(identified->declarationId().specialization()).information().toString();
|
|
QCOMPARE(specializationString, QString("<int>"));
|
|
QCOMPARE(top->localDeclarations()[3]->abstractType()->toString().remove(' '), QString("Test<int>::It"));
|
|
QCOMPARE(TypeUtils::unAliasedType(top->localDeclarations()[3]->abstractType())->toString().remove(' '), QString("I<int>"));
|
|
|
|
lock.unlock();
|
|
parse(method, DumpNone, 0, KUrl(), top);
|
|
lock.lock();
|
|
|
|
QCOMPARE(top->localDeclarations().count(), 4);
|
|
QVERIFY(top->localDeclarations()[3]->abstractType());
|
|
QCOMPARE(TypeUtils::unAliasedType(top->localDeclarations()[3]->abstractType())->toString().remove(' '), QString("I<int>"));
|
|
|
|
release(top);
|
|
}
|
|
{
|
|
QByteArray method("template<class T> class Test { public: T member; typedef T Data; enum { Value = 3 }; }; typedef Test<int> IntTest; void test() { IntTest tv; int i = Test<int>::Value; }");
|
|
TopDUContext* top = parse(method, DumpNone);
|
|
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(CompletionItemTester(top->childContexts()[3], "Test<int>::").names.toSet(), QSet<QString>() << "Data" << "Value" << "member");
|
|
|
|
lock.unlock();
|
|
parse(method, DumpNone, 0, KUrl(), top);
|
|
lock.lock();
|
|
|
|
QCOMPARE(top->childContexts().count(), 4);
|
|
QCOMPARE(top->childContexts()[3]->type(), DUContext::Other);
|
|
QCOMPARE(CompletionItemTester(top->childContexts()[3], "IntTest::").names.toSet(), QSet<QString>() << "Data" << "Value" << "member");
|
|
QCOMPARE(CompletionItemTester(top->childContexts()[3], "Test<int>::").names.toSet(), QSet<QString>() << "Data" << "Value" << "member");
|
|
QCOMPARE(CompletionItemTester(top->childContexts()[3], "tv.").names.toSet(), QSet<QString>() << "member");
|
|
release(top);
|
|
}
|
|
}
|
|
|
|
void TestCppCodeCompletion::testNamespaceCompletion() {
|
|
|
|
QByteArray method("namespace A { class m; namespace Q {}; }; namespace A { class n; int q; }");
|
|
TopDUContext* top = parse(method, DumpNone);
|
|
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
|
|
QCOMPARE(top->localDeclarations().count(), 2);
|
|
QCOMPARE(top->childContexts().count(), 2);
|
|
QCOMPARE(top->localDeclarations()[0]->identifier(), Identifier("A"));
|
|
QCOMPARE(top->localDeclarations()[1]->identifier(), Identifier("A"));
|
|
QCOMPARE(top->localDeclarations()[0]->kind(), Declaration::Namespace);
|
|
QCOMPARE(top->localDeclarations()[1]->kind(), Declaration::Namespace);
|
|
QVERIFY(!top->localDeclarations()[0]->abstractType());
|
|
QVERIFY(!top->localDeclarations()[1]->abstractType());
|
|
QCOMPARE(top->localDeclarations()[0]->internalContext(), top->childContexts()[0]);
|
|
QCOMPARE(top->localDeclarations()[1]->internalContext(), top->childContexts()[1]);
|
|
|
|
QCOMPARE(CompletionItemTester(top).names, QStringList() << "A");
|
|
|
|
QCOMPARE(CompletionItemTester(top->childContexts()[1], "A::").names.toSet(), QSet<QString>() << "m" << "n" << "Q");
|
|
QCOMPARE(CompletionItemTester(top).itemData("A", KTextEditor::CodeCompletionModel::Prefix).toString(), QString("namespace"));
|
|
release(top);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testNamespaceAliasCompletion() {
|
|
|
|
QByteArray method("namespace A { class C_A1; class C_A2; namespace Q { class C_Q1; class C_Q2; }; }; "
|
|
"namespace B = A; " // direct import of a namespace
|
|
"namespace C = B; " // indirect import through another alias
|
|
);
|
|
|
|
TopDUContext* top = parse(method, DumpNone);
|
|
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
|
|
QCOMPARE(top->localDeclarations().count(), 3);
|
|
QCOMPARE(top->childContexts().count(), 1);
|
|
QCOMPARE(top->localDeclarations()[0]->identifier(), Identifier("A"));
|
|
QCOMPARE(top->localDeclarations()[1]->identifier(), Identifier("B"));
|
|
QCOMPARE(top->localDeclarations()[2]->identifier(), Identifier("C"));
|
|
QCOMPARE(top->localDeclarations()[0]->kind(), Declaration::Namespace);
|
|
QCOMPARE(top->localDeclarations()[1]->kind(), Declaration::NamespaceAlias);
|
|
QCOMPARE(top->localDeclarations()[2]->kind(), Declaration::NamespaceAlias);
|
|
QVERIFY(!top->localDeclarations()[0]->abstractType());
|
|
QVERIFY(!top->localDeclarations()[1]->abstractType());
|
|
QVERIFY(!top->localDeclarations()[2]->abstractType());
|
|
QCOMPARE(top->localDeclarations()[0]->internalContext(), top->childContexts()[0]);
|
|
|
|
QCOMPARE(CompletionItemTester(top).names.toSet(), QSet<QString>() << "A" << "B" << "C");
|
|
|
|
QCOMPARE(CompletionItemTester(top->childContexts()[0], "A::").names.toSet(), QSet<QString>() << "C_A1" << "C_A2" << "Q");
|
|
QCOMPARE(CompletionItemTester(top->childContexts()[0], "B::").names.toSet(), QSet<QString>() << "C_A1" << "C_A2" << "Q");
|
|
QCOMPARE(CompletionItemTester(top->childContexts()[0], "C::").names.toSet(), QSet<QString>() << "C_A1" << "C_A2" << "Q");
|
|
QCOMPARE(CompletionItemTester(top).itemData("A", KTextEditor::CodeCompletionModel::Prefix).toString(), QString("namespace"));
|
|
release(top);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testNamespaceAliasCycleCompletion() {
|
|
|
|
QByteArray method("namespace A { class C_A1; class C_A2; namespace Q { class C_Q1; class C_Q2; }; }; "
|
|
"namespace B = A; namespace A = B; ");
|
|
|
|
TopDUContext* top = parse(method, DumpNone);
|
|
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
|
|
QCOMPARE(top->localDeclarations().count(), 3);
|
|
QCOMPARE(top->childContexts().count(), 1);
|
|
QCOMPARE(top->localDeclarations()[0]->identifier(), Identifier("A"));
|
|
QCOMPARE(top->localDeclarations()[1]->identifier(), Identifier("B"));
|
|
QCOMPARE(top->localDeclarations()[2]->identifier(), Identifier("A"));
|
|
QCOMPARE(top->localDeclarations()[0]->kind(), Declaration::Namespace);
|
|
QCOMPARE(top->localDeclarations()[1]->kind(), Declaration::NamespaceAlias);
|
|
QCOMPARE(top->localDeclarations()[2]->kind(), Declaration::NamespaceAlias);
|
|
QVERIFY(!top->localDeclarations()[0]->abstractType());
|
|
QVERIFY(!top->localDeclarations()[1]->abstractType());
|
|
QVERIFY(!top->localDeclarations()[2]->abstractType());
|
|
QCOMPARE(top->localDeclarations()[0]->internalContext(), top->childContexts()[0]);
|
|
|
|
QCOMPARE(CompletionItemTester(top).names.toSet(), QSet<QString>() << "A" << "B");
|
|
|
|
QCOMPARE(CompletionItemTester(top->childContexts()[0], "A::").names.toSet(), QSet<QString>() << "C_A1" << "C_A2" << "Q");
|
|
QCOMPARE(CompletionItemTester(top->childContexts()[0], "B::").names.toSet(), QSet<QString>() << "C_A1" << "C_A2" << "Q");
|
|
QCOMPARE(CompletionItemTester(top).itemData("A", KTextEditor::CodeCompletionModel::Prefix).toString(), QString("namespace"));
|
|
release(top);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testAfterNamespace()
|
|
{
|
|
QByteArray method("void foo(); namespace asdf { namespace foobar {} }");
|
|
|
|
TopDUContext* top = parse(method, DumpNone);
|
|
|
|
DUChainWriteLocker lock;
|
|
|
|
CompletionItemTester tester(top, "namespace");
|
|
QCOMPARE(tester.names, QStringList() << "asdf");
|
|
release(top);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testIndirectImports()
|
|
{
|
|
{
|
|
addInclude("testIndirectImportsHeader1.h", "class C {};");
|
|
addInclude("testIndirectImportsHeader2.h", "template<class T> class D : public T {};");
|
|
|
|
QByteArray method("#include \"testIndirectImportsHeader2.h\"\n#include \"testIndirectImportsHeader1.h\"\n typedef D<C> Base; class MyClass : public C, public Base {}; ");
|
|
|
|
TopDUContext* top = parse(method, DumpNone);
|
|
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
|
|
QCOMPARE(top->importedParentContexts().size(), 2);
|
|
QCOMPARE(top->childContexts().count(), 1);
|
|
QCOMPARE(top->childContexts()[0]->importedParentContexts().count(), 2);
|
|
QVERIFY(!top->childContexts()[0]->importedParentContexts()[0].isDirect()); //Should not be direct, since it crosses a header
|
|
QVERIFY(!top->childContexts()[0]->importedParentContexts()[0].indirectDeclarationId().isDirect()); //Should not be direct, since it crosses a header
|
|
QVERIFY(!top->childContexts()[0]->importedParentContexts()[1].isDirect()); //Should not be direct, since it crosses a header
|
|
QVERIFY(!top->childContexts()[0]->importedParentContexts()[1].indirectDeclarationId().isDirect()); //Should not be direct, since it crosses a header
|
|
DUContext* import1 = top->childContexts()[0]->importedParentContexts()[1].context(top);
|
|
QVERIFY(import1);
|
|
QCOMPARE(import1->importedParentContexts().count(), 2); //The template-context is also imported
|
|
QVERIFY(!import1->importedParentContexts()[1].isDirect()); //Should not be direct, since it crosses a header
|
|
QVERIFY(!import1->importedParentContexts()[1].indirectDeclarationId().isDirect()); //Should not be direct, since it crosses a header
|
|
|
|
DUContext* import2 = import1->importedParentContexts()[0].context(top);
|
|
QVERIFY(import2);
|
|
QCOMPARE(import2->importedParentContexts().count(), 0);
|
|
|
|
release(top);
|
|
}
|
|
}
|
|
|
|
void TestCppCodeCompletion::testSameNamespace() {
|
|
{
|
|
addInclude("testSameNamespaceClassHeader.h", "namespace A {\n class B\n {\n \n};\n \n}");
|
|
|
|
QByteArray method("#include \"testSameNamespaceClassHeader.h\"\n namespace A {\n namespace AA {\n};\n };\n");
|
|
|
|
TopDUContext* top = parse(method, DumpNone);
|
|
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
|
|
QCOMPARE(top->importedParentContexts().size(), 1);
|
|
QVERIFY(!top->parentContext());
|
|
QCOMPARE(top->childContexts().count(), 1);
|
|
QCOMPARE(top->childContexts()[0]->childContexts().count(), 1);
|
|
{
|
|
kDebug() << CompletionItemTester(top->childContexts()[0]).names;
|
|
QCOMPARE(CompletionItemTester(top->childContexts()[0]).names.toSet(), QSet<QString>() << "B" << "A" << "AA");
|
|
QCOMPARE(CompletionItemTester(top->childContexts()[0]->childContexts()[0]).names.toSet(), QSet<QString>() << "B" << "A" << "AA");
|
|
}
|
|
|
|
release(top);
|
|
}
|
|
|
|
{
|
|
// 0 1 2 3 4 5 6 7
|
|
// 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012
|
|
QByteArray method("namespace A { class C { }; void test2() {} } namespace A { void test() { } class C {};}");
|
|
|
|
TopDUContext* top = parse(method, DumpNone);
|
|
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
|
|
QVERIFY(!top->parentContext());
|
|
QCOMPARE(top->childContexts().count(), 2);
|
|
QCOMPARE(top->childContexts()[1]->childContexts().count(), 3);
|
|
QCOMPARE(top->childContexts()[1]->localDeclarations().count(), 2);
|
|
FunctionDefinition* funDef = dynamic_cast<KDevelop::FunctionDefinition*>(top->childContexts()[1]->localDeclarations()[0]);
|
|
QVERIFY(!funDef->hasDeclaration());
|
|
|
|
// lock.unlock();
|
|
{
|
|
kDebug() << CompletionItemTester(top->childContexts()[1]->childContexts()[2]).names;
|
|
QCOMPARE(CompletionItemTester(top->childContexts()[1]->childContexts()[2]).names.toSet(), QSet<QString>() << "C" << "A");
|
|
QCOMPARE(CompletionItemTester(top->childContexts()[1]).names.toSet(), QSet<QString>() << "C" << "A");
|
|
QCOMPARE(CompletionItemTester(top->childContexts()[1]->childContexts()[1]).names.toSet(), QSet<QString>() << "C" << "test2" << "test" << "A");
|
|
}
|
|
|
|
release(top);
|
|
}
|
|
}
|
|
|
|
void TestCppCodeCompletion::testUnnamedNamespace() {
|
|
TEST_FILE_PARSE_ONLY
|
|
|
|
// 0 1 2 3 4 5 6 7
|
|
// 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012
|
|
QByteArray method("namespace {int a;} namespace { int b; }; void test() {a = 3;}");
|
|
|
|
TopDUContext* top = parse(method, DumpNone);
|
|
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
|
|
QVERIFY(!top->parentContext());
|
|
QCOMPARE(top->childContexts().count(), 4);
|
|
QCOMPARE(top->localDeclarations().count(), 3);
|
|
kDebug() << top->localDeclarations()[0]->range().castToSimpleRange().textRange();
|
|
QCOMPARE(top->localDeclarations()[0]->range().castToSimpleRange().textRange(), KTextEditor::Range(0, 10, 0, 10));
|
|
QVERIFY(findDeclaration(top, QualifiedIdentifier("a")));
|
|
QVERIFY(findDeclaration(top, QualifiedIdentifier("b")));
|
|
QVERIFY(findDeclaration(top, QualifiedIdentifier("a"))->uses().size());
|
|
PersistentSymbolTable::FilteredDeclarationIterator decls = KDevelop::PersistentSymbolTable::self().getFilteredDeclarations(QualifiedIdentifier(Cpp::unnamedNamespaceIdentifier().identifier()), top->recursiveImportIndices());
|
|
QVERIFY(decls);
|
|
QCOMPARE(top->findLocalDeclarations(Cpp::unnamedNamespaceIdentifier().identifier()).size(), 2);
|
|
QCOMPARE(top->findDeclarations(QualifiedIdentifier(Cpp::unnamedNamespaceIdentifier().identifier())).size(), 2);
|
|
|
|
// lock.unlock();
|
|
{
|
|
Cpp::CodeCompletionContext::Ptr cptr( new Cpp::CodeCompletionContext(DUContextPointer(top), "; ", QString(), top->range().end) );
|
|
bool abort = false;
|
|
typedef KSharedPtr <KDevelop::CompletionTreeItem > Item;
|
|
|
|
QList <Item > items = cptr->completionItems(abort);
|
|
foreach(Item i, items) {
|
|
Cpp::NormalDeclarationCompletionItem* decItem = dynamic_cast<Cpp::NormalDeclarationCompletionItem*>(i.data());
|
|
QVERIFY(decItem);
|
|
kDebug() << decItem->declaration()->toString();
|
|
kDebug() << i->data(fakeModel().index(0, KTextEditor::CodeCompletionModel::Name), Qt::DisplayRole, 0).toString();
|
|
}
|
|
|
|
//Have been filtered out, because only types are shown from the global scope
|
|
QCOMPARE(items.count(), 0); //C, test, and i
|
|
}
|
|
{
|
|
Cpp::CodeCompletionContext::Ptr cptr( new Cpp::CodeCompletionContext(DUContextPointer(top->childContexts()[3]), "; ", QString(), top->range().end) );
|
|
bool abort = false;
|
|
typedef KSharedPtr <KDevelop::CompletionTreeItem > Item;
|
|
|
|
QList <Item > items = cptr->completionItems(abort);
|
|
foreach(Item i, items) {
|
|
Cpp::NormalDeclarationCompletionItem* decItem = dynamic_cast<Cpp::NormalDeclarationCompletionItem*>(i.data());
|
|
QVERIFY(decItem);
|
|
kDebug() << decItem->declaration()->toString();
|
|
kDebug() << i->data(fakeModel().index(0, KTextEditor::CodeCompletionModel::Name), Qt::DisplayRole, 0).toString();
|
|
}
|
|
|
|
QCOMPARE(items.count(), 3); //b, a, and test
|
|
}
|
|
|
|
// lock.lock();
|
|
release(top);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void TestCppCodeCompletion::testCompletionContext() {
|
|
TEST_FILE_PARSE_ONLY
|
|
|
|
QByteArray test = "#include \"testFile1.h\"\n";
|
|
test += "#include \"testFile2.h\"\n";
|
|
test += "void test() { }";
|
|
|
|
DUContext* context = parse( test, DumpNone /*DumpDUChain | DumpAST */);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
|
|
QVERIFY(context->childContexts().count());
|
|
DUContext* testContext = context->childContexts()[0];
|
|
QCOMPARE( testContext->type(), DUContext::Function );
|
|
|
|
lock.unlock();
|
|
{
|
|
///Test whether a recursive function-call context is created correctly
|
|
Cpp::CodeCompletionContext::Ptr cptr( new Cpp::CodeCompletionContext(DUContextPointer(DUContextPointer(context)), "; globalFunction(globalFunction(globalHonk, ", QString(), CursorInRevision::invalid() ) );
|
|
Cpp::CodeCompletionContext& c(*cptr);
|
|
QVERIFY( c.isValid() );
|
|
QVERIFY( c.accessType() == Cpp::CodeCompletionContext::NoMemberAccess );
|
|
QVERIFY( !c.memberAccessContainer().isValid() );
|
|
|
|
//globalHonk is of type Honk. Check whether all matching functions were rated correctly by the overload-resolution.
|
|
//The preferred parent-function in the list should be "Honk globalFunction( const Honk&, const Heinz& h )", because the first argument maches globalHonk
|
|
Cpp::CodeCompletionContext* function = c.parentContext();
|
|
QVERIFY(function);
|
|
QVERIFY(function->accessType() == Cpp::CodeCompletionContext::FunctionCallAccess);
|
|
QVERIFY(!function->functions().isEmpty());
|
|
|
|
lock.lock();
|
|
for( Cpp::CodeCompletionContext::FunctionList::const_iterator it = function->functions().begin(); it != function->functions().end(); ++it )
|
|
kDebug(9007) << (*it).function.declaration()->toString() << ((*it).function.isViable() ? QString("(viable)") : QString("(not viable)")) ;
|
|
lock.unlock();
|
|
|
|
QCOMPARE(function->functions().size(), 4);
|
|
QVERIFY(function->functions()[0].function.isViable());
|
|
//Because Honk has a conversion-function to int, globalFunction(int) is yet viable(although it can take only 1 parameter)
|
|
QVERIFY(function->functions()[1].function.isViable());
|
|
//Because Erna has a constructor that takes "const Honk&", globalFunction(Erna) is yet viable(although it can take only 1 parameter)
|
|
QVERIFY(function->functions()[2].function.isViable());
|
|
//Because a value of type Honk is given, 2 globalFunction's are not viable
|
|
QVERIFY(!function->functions()[3].function.isViable());
|
|
|
|
|
|
function = function->parentContext();
|
|
QVERIFY(function);
|
|
QVERIFY(function->accessType() == Cpp::CodeCompletionContext::FunctionCallAccess);
|
|
QVERIFY(!function->functions().isEmpty());
|
|
QVERIFY(!function->parentContext());
|
|
QVERIFY(function->functions().size() == 4);
|
|
//Because no arguments were given, all functions are viable
|
|
QVERIFY(function->functions()[0].function.isViable());
|
|
QVERIFY(function->functions()[1].function.isViable());
|
|
QVERIFY(function->functions()[2].function.isViable());
|
|
QVERIFY(function->functions()[3].function.isViable());
|
|
}
|
|
|
|
{
|
|
///The context is a function, and there is no prefix-expression, so it should be normal completion.
|
|
DUContextPointer contPtr(context);
|
|
Cpp::CodeCompletionContext c(contPtr, "{", QString(), CursorInRevision::invalid() );
|
|
QVERIFY( c.isValid() );
|
|
QVERIFY( c.accessType() == Cpp::CodeCompletionContext::NoMemberAccess );
|
|
QVERIFY( !c.memberAccessContainer().isValid() );
|
|
}
|
|
|
|
lock.lock();
|
|
release(context);
|
|
}
|
|
|
|
|
|
void TestCppCodeCompletion::testTypeConversion() {
|
|
TEST_FILE_PARSE_ONLY
|
|
|
|
QByteArray test = "#include \"testFile1.h\"\n";
|
|
test += "#include \"testFile2.h\"\n";
|
|
test += "#include \"testFile3.h\"\n";
|
|
test += "int n;\n";
|
|
test += "void test() { }\n";
|
|
|
|
DUContext* context = parse( test, DumpNone /*DumpDUChain | DumpAST */);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
|
|
DUContext* testContext = context->childContexts()[0];
|
|
QCOMPARE( testContext->type(), DUContext::Function );
|
|
|
|
QVERIFY(findDeclaration( testContext, QualifiedIdentifier("Heinz") ));
|
|
QVERIFY(findDeclaration( testContext, QualifiedIdentifier("Erna") ));
|
|
Declaration* decl = findDeclaration( testContext, QualifiedIdentifier("Erna") );
|
|
QVERIFY(decl);
|
|
QVERIFY(decl->logicalInternalContext(context->topContext()));
|
|
QVERIFY(findDeclaration( testContext, QualifiedIdentifier("Honk") ));
|
|
QVERIFY(findDeclaration( testContext, QualifiedIdentifier("A") ));
|
|
QVERIFY(findDeclaration( testContext, QualifiedIdentifier("B") ));
|
|
QVERIFY(findDeclaration( testContext, QualifiedIdentifier("test") ));
|
|
QVERIFY(findDeclaration( testContext, QualifiedIdentifier("n") ));
|
|
AbstractType::Ptr Heinz = findDeclaration( testContext, QualifiedIdentifier("Heinz") )->abstractType();
|
|
AbstractType::Ptr Erna = findDeclaration( testContext, QualifiedIdentifier("Erna") )->abstractType();
|
|
AbstractType::Ptr Honk = findDeclaration( testContext, QualifiedIdentifier("Honk") )->abstractType();
|
|
AbstractType::Ptr A = findDeclaration( testContext, QualifiedIdentifier("A") )->abstractType();
|
|
AbstractType::Ptr B = findDeclaration( testContext, QualifiedIdentifier("B") )->abstractType();
|
|
AbstractType::Ptr n = findDeclaration( testContext, QualifiedIdentifier("n") )->abstractType();
|
|
|
|
QVERIFY(n);
|
|
|
|
{
|
|
FunctionType::Ptr test = findDeclaration( testContext, QualifiedIdentifier("test") )->type<FunctionType>();
|
|
QVERIFY(test);
|
|
|
|
Cpp::TypeConversion conv(context->topContext());
|
|
QVERIFY(!conv.implicitConversion(test->returnType()->indexed(), Heinz->indexed(), false));
|
|
QVERIFY(!conv.implicitConversion(Heinz->indexed(), test->returnType()->indexed(), false));
|
|
QVERIFY(!conv.implicitConversion(test->returnType()->indexed(), n->indexed(), false));
|
|
QVERIFY(!conv.implicitConversion(n->indexed(), test->returnType()->indexed(), false));
|
|
}
|
|
//lock.unlock();
|
|
{
|
|
///Test whether a recursive function-call context is created correctly
|
|
Cpp::TypeConversion conv(context->topContext());
|
|
QVERIFY( !conv.implicitConversion(Honk->indexed(), Heinz->indexed()) );
|
|
QVERIFY( conv.implicitConversion(Honk->indexed(), typeInt->indexed()) ); //Honk has operator int()
|
|
QVERIFY( conv.implicitConversion(Honk->indexed(), Erna->indexed()) ); //Erna has constructor that takes Honk
|
|
QVERIFY( !conv.implicitConversion(Erna->indexed(), Heinz->indexed()) );
|
|
|
|
///@todo reenable once base-classes are parsed correctly
|
|
//QVERIFY( conv.implicitConversion(B, A) ); //B is based on A, so there is an implicit copy-constructor that creates A from B
|
|
//QVERIFY( conv.implicitConversion(Heinz, Erna) ); //Heinz is based on Erna, so there is an implicit copy-constructor that creates Erna from Heinz
|
|
|
|
}
|
|
|
|
//lock.lock();
|
|
release(context);
|
|
}
|
|
|
|
KDevelop::IndexedType toReference(IndexedType t) {
|
|
|
|
ReferenceType::Ptr refType( new ReferenceType);
|
|
refType->setBaseType(t.abstractType());
|
|
return refType->indexed();
|
|
}
|
|
|
|
KDevelop::IndexedType toPointer(IndexedType t) {
|
|
|
|
PointerType::Ptr refType( new PointerType);
|
|
refType->setBaseType(t.abstractType());
|
|
return refType->indexed();
|
|
}
|
|
|
|
void TestCppCodeCompletion::testTypeConversion2() {
|
|
{
|
|
QByteArray test = "class A {}; class B {public: explicit B(const A&); explicit B(const int&){}; private: operator A() const {}; }; class C : public B{private: C(B) {}; };";
|
|
TopDUContext* context = parse( test, DumpAll /*DumpDUChain | DumpAST */);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(context->localDeclarations().size(), 3);
|
|
QVERIFY(context->localDeclarations()[0]->internalContext());
|
|
QCOMPARE(context->localDeclarations()[1]->internalContext()->localDeclarations().count(), 3);
|
|
ClassFunctionDeclaration* classFun = dynamic_cast<ClassFunctionDeclaration*>(context->localDeclarations()[1]->internalContext()->localDeclarations()[0]);
|
|
QVERIFY(classFun);
|
|
QVERIFY(classFun->isExplicit());
|
|
classFun = dynamic_cast<ClassFunctionDeclaration*>(context->localDeclarations()[1]->internalContext()->localDeclarations()[1]);
|
|
QVERIFY(classFun);
|
|
QVERIFY(classFun->isExplicit());
|
|
|
|
|
|
Cpp::TypeConversion conv(context);
|
|
QVERIFY( !conv.implicitConversion(context->localDeclarations()[2]->indexedType(), context->localDeclarations()[0]->indexedType()) );
|
|
QVERIFY( conv.implicitConversion(context->localDeclarations()[2]->indexedType(), context->localDeclarations()[1]->indexedType()) );
|
|
QVERIFY( !conv.implicitConversion(context->localDeclarations()[1]->indexedType(), context->localDeclarations()[2]->indexedType()) );
|
|
QVERIFY( !conv.implicitConversion(context->localDeclarations()[1]->indexedType(), context->localDeclarations()[0]->indexedType()) );
|
|
QVERIFY( !conv.implicitConversion(context->localDeclarations()[0]->indexedType(), context->localDeclarations()[1]->indexedType()) );
|
|
|
|
QVERIFY( !conv.implicitConversion(toReference(context->localDeclarations()[2]->indexedType()), toReference(context->localDeclarations()[0]->indexedType()) ));
|
|
QVERIFY( conv.implicitConversion(toReference(context->localDeclarations()[2]->indexedType()), toReference(context->localDeclarations()[1]->indexedType()) ));
|
|
QVERIFY( !conv.implicitConversion(toReference(context->localDeclarations()[1]->indexedType()), toReference(context->localDeclarations()[2]->indexedType()) ));
|
|
QVERIFY( !conv.implicitConversion(toReference(context->localDeclarations()[1]->indexedType()), toReference(context->localDeclarations()[0]->indexedType()) ));
|
|
QVERIFY( !conv.implicitConversion(toReference(context->localDeclarations()[0]->indexedType()), toReference(context->localDeclarations()[1]->indexedType()) ));
|
|
|
|
QVERIFY( !conv.implicitConversion(toPointer(context->localDeclarations()[2]->indexedType()), toPointer(context->localDeclarations()[0]->indexedType()) ));
|
|
QVERIFY( conv.implicitConversion(toPointer(context->localDeclarations()[2]->indexedType()), toPointer(context->localDeclarations()[1]->indexedType()) ));
|
|
QVERIFY( !conv.implicitConversion(toPointer(context->localDeclarations()[1]->indexedType()), toPointer(context->localDeclarations()[2]->indexedType()) ));
|
|
QVERIFY( !conv.implicitConversion(toPointer(context->localDeclarations()[1]->indexedType()), toPointer(context->localDeclarations()[0]->indexedType()) ));
|
|
QVERIFY( !conv.implicitConversion(toPointer(context->localDeclarations()[0]->indexedType()), toPointer(context->localDeclarations()[1]->indexedType()) ));
|
|
|
|
release(context);
|
|
}
|
|
{
|
|
QByteArray test = "const char** b; char** c; char** const d; char* const * e; char f; const char q; ";
|
|
TopDUContext* context = parse( test, DumpNone /*DumpDUChain | DumpAST */);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(context->localDeclarations().size(), 6);
|
|
Cpp::TypeConversion conv(context);
|
|
QVERIFY( !conv.implicitConversion(context->localDeclarations()[0]->indexedType(), context->localDeclarations()[1]->indexedType()) );
|
|
PointerType::Ptr fromPointer = context->localDeclarations()[1]->indexedType().type< PointerType>();
|
|
QVERIFY(fromPointer);
|
|
QVERIFY( !(fromPointer->modifiers() & AbstractType::ConstModifier));
|
|
QVERIFY( conv.implicitConversion(context->localDeclarations()[1]->indexedType(), context->localDeclarations()[0]->indexedType()) );
|
|
QVERIFY( conv.implicitConversion(context->localDeclarations()[1]->indexedType(), context->localDeclarations()[2]->indexedType()) );
|
|
QVERIFY( !conv.implicitConversion(context->localDeclarations()[0]->indexedType(), context->localDeclarations()[2]->indexedType()) );
|
|
QVERIFY( conv.implicitConversion(context->localDeclarations()[2]->indexedType(), context->localDeclarations()[0]->indexedType()) );
|
|
QVERIFY( conv.implicitConversion(context->localDeclarations()[2]->indexedType(), context->localDeclarations()[1]->indexedType()) );
|
|
QVERIFY( !conv.implicitConversion(context->localDeclarations()[3]->indexedType(), context->localDeclarations()[0]->indexedType()) );
|
|
QVERIFY( !conv.implicitConversion(context->localDeclarations()[3]->indexedType(), context->localDeclarations()[1]->indexedType()) );
|
|
QVERIFY( !conv.implicitConversion(context->localDeclarations()[0]->indexedType(), context->localDeclarations()[3]->indexedType()) );
|
|
QVERIFY( conv.implicitConversion(context->localDeclarations()[1]->indexedType(), context->localDeclarations()[3]->indexedType()) );
|
|
QVERIFY( !conv.implicitConversion(context->localDeclarations()[3]->indexedType(), context->localDeclarations()[1]->indexedType()) );
|
|
QVERIFY( conv.implicitConversion(context->localDeclarations()[4]->indexedType(), context->localDeclarations()[5]->indexedType()) );
|
|
QVERIFY( conv.implicitConversion(context->localDeclarations()[5]->indexedType(), context->localDeclarations()[4]->indexedType()) );
|
|
|
|
release(context);
|
|
}
|
|
|
|
{
|
|
QByteArray test = "class A {}; class C {}; enum M { Em }; template<class T> class B{ public:B(T t); }; ";
|
|
TopDUContext* context = parse( test, DumpNone /*DumpDUChain | DumpAST */);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(context->localDeclarations().size(), 4);
|
|
QCOMPARE(context->childContexts().size(), 5);
|
|
Cpp::TypeConversion conv(context);
|
|
Declaration* decl = findDeclaration(context, QualifiedIdentifier("B<A>"));
|
|
QVERIFY(decl);
|
|
kDebug() << decl->toString();
|
|
|
|
QVERIFY( conv.implicitConversion(context->localDeclarations()[0]->indexedType(), decl->indexedType()) );
|
|
|
|
decl = findDeclaration(context, QualifiedIdentifier("B<M>"));
|
|
QVERIFY(decl);
|
|
kDebug() << decl->toString();
|
|
QCOMPARE(context->childContexts()[2]->localDeclarations().size(), 1);
|
|
QVERIFY( conv.implicitConversion(context->childContexts()[2]->localDeclarations()[0]->indexedType(), decl->indexedType()) );
|
|
|
|
release(context);
|
|
}
|
|
}
|
|
|
|
void TestCppCodeCompletion::testInclude() {
|
|
TEST_FILE_PARSE_ONLY
|
|
|
|
addInclude("file1.h", "#include \"testFile1.h\"\n#include \"testFile2.h\"\n");
|
|
|
|
|
|
QByteArray test = "#include \"file1.h\" \n struct Cont { operator int() {}; }; void test( int c = 5 ) { this->test( Cont(), 1, 5.5, 6); }; HONK undefinedHonk;";
|
|
DUContext* c = parse( test, DumpNone /*DumpDUChain | DumpAST */);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
|
|
QVERIFY(c->topContext()->usingImportsCache());
|
|
|
|
Declaration* decl = findDeclaration(c, QualifiedIdentifier("globalHeinz"));
|
|
QVERIFY(decl);
|
|
QVERIFY(decl->abstractType());
|
|
QCOMPARE(decl->abstractType()->toString(), QString("Heinz"));
|
|
|
|
decl = findDeclaration(c, QualifiedIdentifier("globalErna"));
|
|
QVERIFY(decl);
|
|
QVERIFY(decl->abstractType());
|
|
QCOMPARE(decl->abstractType()->toString(), QString("Erna"));
|
|
|
|
decl = findDeclaration(c, QualifiedIdentifier("globalInt"));
|
|
QVERIFY(decl);
|
|
QVERIFY(decl->abstractType());
|
|
QCOMPARE(decl->abstractType()->toString(), QString("int"));
|
|
|
|
decl = findDeclaration(c, QualifiedIdentifier("Honk"));
|
|
QVERIFY(decl);
|
|
QVERIFY(decl->abstractType());
|
|
QCOMPARE(decl->abstractType()->toString(), QString("Honk"));
|
|
|
|
decl = findDeclaration(c, QualifiedIdentifier("honky"));
|
|
QVERIFY(decl);
|
|
QVERIFY(decl->abstractType());
|
|
QCOMPARE(decl->abstractType()->toString(), QString("Honk"));
|
|
|
|
decl = findDeclaration(c, QualifiedIdentifier("globalHonk"));
|
|
QVERIFY(decl);
|
|
QVERIFY(decl->abstractType());
|
|
QCOMPARE(decl->abstractType()->toString(), QString("Honk"));
|
|
|
|
decl = findDeclaration(c, QualifiedIdentifier("globalMacroHonk"));
|
|
QVERIFY(decl);
|
|
QVERIFY(decl->abstractType());
|
|
QCOMPARE(decl->abstractType()->toString(), QString("Honk"));
|
|
|
|
///HONK was #undef'ed in testFile2, so this must be unresolved.
|
|
decl = findDeclaration(c, QualifiedIdentifier("undefinedHonk"));
|
|
QVERIFY(decl);
|
|
QVERIFY(decl->abstractType().cast<DelayedType>());
|
|
|
|
|
|
Cpp::ExpressionParser parser;
|
|
|
|
///The following test tests the expression-parser, but it is here because the other tests depend on it
|
|
lock.unlock();
|
|
Cpp::ExpressionEvaluationResult result = parser.evaluateExpression( "globalHonk.erna", DUContextPointer( c ) );
|
|
lock.lock();
|
|
|
|
QVERIFY(result.isValid());
|
|
QVERIFY(result.isInstance);
|
|
QVERIFY(result.type);
|
|
QCOMPARE(result.type.abstractType()->toString(), QString("Erna&"));
|
|
|
|
|
|
///Test overload-resolution
|
|
lock.unlock();
|
|
result = parser.evaluateExpression( "globalClass.function(globalHeinz)", DUContextPointer(c));
|
|
lock.lock();
|
|
|
|
QVERIFY(result.isValid());
|
|
QVERIFY(result.isInstance);
|
|
QVERIFY(result.type);
|
|
QCOMPARE(result.type.abstractType()->toString(), QString("Heinz"));
|
|
|
|
lock.unlock();
|
|
result = parser.evaluateExpression( "globalClass.function(globalHonk.erna)", DUContextPointer(c));
|
|
lock.lock();
|
|
|
|
QVERIFY(result.isValid());
|
|
QVERIFY(result.isInstance);
|
|
QVERIFY(result.type);
|
|
QCOMPARE(result.type.abstractType()->toString(), QString("Erna"));
|
|
|
|
//No matching function for type Honk. Since the expression-parser is not set to "strict", it returns any found function with the right name.
|
|
lock.unlock();
|
|
result = parser.evaluateExpression( "globalClass.function(globalHonk)", DUContextPointer(c));
|
|
lock.lock();
|
|
|
|
QVERIFY(result.isValid());
|
|
QVERIFY(result.isInstance);
|
|
QVERIFY(result.type);
|
|
//QCOMPARE(result.type.abstractType()->toString(), QString("Heinz"));
|
|
|
|
|
|
lock.unlock();
|
|
result = parser.evaluateExpression( "globalFunction(globalHeinz)", DUContextPointer(c));
|
|
lock.lock();
|
|
|
|
QVERIFY(result.isValid());
|
|
QVERIFY(result.isInstance);
|
|
QVERIFY(result.type);
|
|
QCOMPARE(result.type.abstractType()->toString(), QString("Heinz"));
|
|
lock.unlock();
|
|
|
|
result = parser.evaluateExpression( "globalFunction(globalHonk.erna)", DUContextPointer(c));
|
|
lock.lock();
|
|
|
|
QVERIFY(result.isValid());
|
|
QVERIFY(result.isInstance);
|
|
QVERIFY(result.type);
|
|
QCOMPARE(result.type.abstractType()->toString(), QString("Erna"));
|
|
|
|
release(c);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testUpdateChain() {
|
|
TEST_FILE_PARSE_ONLY
|
|
|
|
{
|
|
QByteArray text("#define Q_FOREACH(variable, container) for (QForeachContainer<__typeof__(container)> _container_(container); !_container_.brk && _container_.i != _container_.e; __extension__ ({ ++_container_.brk; ++_container_.i; })) for (variable = *_container_.i;; __extension__ ({--_container_.brk; break;})) \nvoid test() { Q_FOREACH(int a, b) { int i; } }");
|
|
TopDUContext* top = parse( text, DumpAll );
|
|
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(top->childContexts().count(), 2);
|
|
QCOMPARE(top->childContexts()[1]->childContexts().count(), 2);
|
|
QCOMPARE(top->childContexts()[1]->childContexts()[1]->childContexts().count(), 2);
|
|
QCOMPARE(top->childContexts()[1]->childContexts()[1]->childContexts()[1]->localDeclarations().count(), 1);
|
|
IndexedDeclaration decl(top->childContexts()[1]->childContexts()[1]->childContexts()[1]->localDeclarations()[0]);
|
|
QVERIFY(decl.data());
|
|
QCOMPARE(decl.data()->identifier().toString(), QString("i"));
|
|
|
|
parse(text, DumpNone, 0, KUrl(), top);
|
|
QVERIFY(decl.data()); //Make sure the declaration has been updated, and not deleted
|
|
|
|
release(top);
|
|
}
|
|
}
|
|
|
|
void TestCppCodeCompletion::testHeaderSections() {
|
|
TEST_FILE_PARSE_ONLY
|
|
/**
|
|
* Make sure that the ends of header-sections are recognized correctly
|
|
* */
|
|
|
|
addInclude( "someHeader.h", "\n" );
|
|
addInclude( "otherHeader.h", "\n" );
|
|
|
|
IncludeFileList includes;
|
|
|
|
IndexedString turl("ths.h");
|
|
|
|
QCOMPARE(preprocess(turl, "#include \"someHeader.h\"\nHello", includes, 0, true), QString("\n"));
|
|
QCOMPARE(includes.count(), 1);
|
|
includes.clear();
|
|
|
|
QCOMPARE(preprocess(turl, "#include \"someHeader.h\"\nHello", includes, 0, false), QString("\nHello"));
|
|
QCOMPARE(includes.count(), 1);
|
|
includes.clear();
|
|
|
|
QCOMPARE(preprocess(turl, "#include \"someHeader.h\"\n#include \"otherHeader.h\"\nHello", includes, 0, false), QString("\n\nHello"));
|
|
QCOMPARE(includes.count(), 2);
|
|
includes.clear();
|
|
|
|
QCOMPARE(preprocess(turl, "#include \"someHeader.h\"\n#include \"otherHeader.h\"\nHello", includes, 0, true), QString("\n\n"));
|
|
QCOMPARE(includes.count(), 2);
|
|
includes.clear();
|
|
|
|
QCOMPARE(preprocess(turl, "#ifndef GUARD\n#define GUARD\n#include \"someHeader.h\"\nHello\n#endif", includes, 0, true), QString("\n\n\n"));
|
|
QCOMPARE(includes.count(), 1);
|
|
includes.clear();
|
|
|
|
QCOMPARE(preprocess(turl, "#ifndef GUARD\n#define GUARD\n#include \"someHeader.h\"\nHello\n#endif", includes, 0, false), QString("\n\n\nHello\n"));
|
|
QCOMPARE(includes.count(), 1);
|
|
includes.clear();
|
|
}
|
|
|
|
void TestCppCodeCompletion::testForwardDeclaration()
|
|
{
|
|
addInclude( "testdeclaration.h", "class Test{ };" );
|
|
QByteArray method("#include \"testdeclaration.h\"\n class Test; ");
|
|
|
|
DUContext* top = parse(method, DumpNone);
|
|
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
|
|
|
|
Declaration* decl = findDeclaration(top, Identifier("Test"), top->range().end);
|
|
QVERIFY(decl);
|
|
QVERIFY(decl->abstractType());
|
|
AbstractType::Ptr t(decl->abstractType());
|
|
QVERIFY(dynamic_cast<const IdentifiedType*>(t.unsafeData()));
|
|
QVERIFY(!decl->isForwardDeclaration());
|
|
|
|
release(top);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testUsesThroughMacros() {
|
|
{
|
|
QByteArray method("int x;\n#define TEST(X) X\ny = TEST(x);");
|
|
|
|
DUContext* top = parse(method, DumpNone);
|
|
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(top->localDeclarations().count(), 1);
|
|
QCOMPARE(top->localDeclarations()[0]->uses().count(), 1);
|
|
QCOMPARE(top->localDeclarations()[0]->uses().begin()->count(), 1);
|
|
QCOMPARE(top->localDeclarations()[0]->uses().begin()->at(0).start.column, 9);
|
|
QCOMPARE(top->localDeclarations()[0]->uses().begin()->at(0).end.column, 10);
|
|
}
|
|
{
|
|
///2 uses of x, that go through the macro TEST(..), and effectively are in line 2 column 5.
|
|
QByteArray method("int x;\n#define TEST(X) void test() { int z = X; int q = X; }\nTEST(x)");
|
|
|
|
kDebug() << method;
|
|
DUContext* top = parse(method, DumpNone);
|
|
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(top->localDeclarations().count(), 2);
|
|
QCOMPARE(top->localDeclarations()[0]->uses().count(), 1);
|
|
//Since uses() returns unique uses, and both uses of x in TEST(x) have the same range,
|
|
//only one use is returned.
|
|
QCOMPARE(top->localDeclarations()[0]->uses().begin()->count(), 1);
|
|
|
|
RangeInRevision range1(top->localDeclarations()[0]->uses().begin()->at(0));
|
|
QCOMPARE(range1.start.line, 2);
|
|
QCOMPARE(range1.end.line, 2);
|
|
QCOMPARE(range1.start.column, 5);
|
|
QCOMPARE(range1.end.column, 6);
|
|
}
|
|
}
|
|
|
|
void TestCppCodeCompletion::testMacroIncludeDirectives()
|
|
{
|
|
addInclude( "macroincludedirectivetest1.h", "class Test1{ };" );
|
|
addInclude( "macro includedirectivetest2.h", "class Test1{ };" );
|
|
|
|
{
|
|
QByteArray method("#define TEST macroincludedirectivetest1.h \n #define TEST_HPP <TEST> \n #include TEST_HPP\n");
|
|
|
|
DUContext* top = parse(method, DumpNone);
|
|
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
|
|
QCOMPARE(top->importedParentContexts().size(), 1);
|
|
|
|
release(top);
|
|
}
|
|
|
|
{
|
|
QByteArray method("#define TEST \"macroincludedirectivetest1.h\" \n #include TEST\n");
|
|
|
|
DUContext* top = parse(method, DumpNone);
|
|
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
|
|
QCOMPARE(top->importedParentContexts().size(), 1);
|
|
|
|
release(top);
|
|
}
|
|
|
|
{
|
|
QByteArray method("#define TEST <macroincludedirectivetest1.h> \n #include TEST\n");
|
|
|
|
DUContext* top = parse(method, DumpNone);
|
|
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
|
|
QCOMPARE(top->importedParentContexts().size(), 1);
|
|
|
|
release(top);
|
|
}
|
|
|
|
{
|
|
QByteArray method("#include \"macro includedirectivetest2.h\"\n");
|
|
|
|
DUContext* top = parse(method, DumpNone);
|
|
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
|
|
QCOMPARE(top->importedParentContexts().size(), 1);
|
|
|
|
release(top);
|
|
}
|
|
|
|
{
|
|
QByteArray method("#define TEST \"macro includedirectivetest2.h\" \n #include TEST\n");
|
|
|
|
DUContext* top = parse(method, DumpNone);
|
|
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
|
|
QCOMPARE(top->importedParentContexts().size(), 1);
|
|
|
|
release(top);
|
|
}
|
|
}
|
|
|
|
void TestCppCodeCompletion::testAcrossHeaderReferences()
|
|
{
|
|
addInclude( "acrossheader1.h", "class Test{ };" );
|
|
addInclude( "acrossheader2.h", "Test t;" );
|
|
QByteArray method("#include \"acrossheader1.h\"\n#include \"acrossheader2.h\"\n");
|
|
|
|
DUContext* top = parse(method, DumpNone);
|
|
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
|
|
|
|
Declaration* decl = findDeclaration(top, Identifier("t"), top->range().end);
|
|
QVERIFY(decl);
|
|
QVERIFY(decl->abstractType());
|
|
AbstractType::Ptr t(decl->abstractType());
|
|
QVERIFY(dynamic_cast<const IdentifiedType*>(t.unsafeData()));
|
|
|
|
release(top);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testAcrossHeaderTemplateResolution() {
|
|
addInclude("acrossheaderresolution1.h", "class C {}; namespace std { template<class T> class A { }; }");
|
|
addInclude("acrossheaderresolution2.h", "namespace std { template<class T> class B { typedef A<T> Type; }; }");
|
|
|
|
QByteArray method("#include \"acrossheaderresolution1.h\"\n#include \"acrossheaderresolution2.h\"\n std::B<C>::Type t;");
|
|
|
|
DUContext* top = parse(method, DumpNone);
|
|
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
|
|
Declaration* decl = findDeclaration(top, QualifiedIdentifier("t"), top->range().end);
|
|
QVERIFY(decl);
|
|
QCOMPARE(QualifiedIdentifier(TypeUtils::unAliasedType(decl->abstractType())->toString()), QualifiedIdentifier("std::A<C>"));
|
|
|
|
release(top);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testAcrossHeaderTemplateReferences()
|
|
{
|
|
addInclude( "acrossheader1.h", "class Dummy { }; template<class Q> class Test{ };" );
|
|
addInclude( "acrossheader2.h", "template<class B, class B2 = Test<B> > class Test2 : public Test<B>{ Test<B> bm; };" );
|
|
QByteArray method("#include \"acrossheader1.h\"\n#include \"acrossheader2.h\"\n ");
|
|
|
|
DUContext* top = parse(method, DumpNone);
|
|
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
|
|
|
|
{
|
|
kDebug() << "top is" << top;
|
|
Declaration* decl = findDeclaration(top, QualifiedIdentifier("Dummy"), top->range().end);
|
|
QVERIFY(decl);
|
|
QVERIFY(decl->abstractType());
|
|
AbstractType::Ptr t(decl->abstractType());
|
|
QVERIFY(dynamic_cast<const IdentifiedType*>(t.unsafeData()));
|
|
QCOMPARE(decl->abstractType()->toString(), QString("Dummy"));
|
|
}
|
|
{
|
|
Declaration* decl = findDeclaration(top, QualifiedIdentifier("Test2<Dummy>::B2"), top->range().end);
|
|
QVERIFY(decl);
|
|
QVERIFY(decl->abstractType());
|
|
AbstractType::Ptr t(decl->abstractType());
|
|
QVERIFY(dynamic_cast<const IdentifiedType*>(t.unsafeData()));
|
|
QCOMPARE(decl->abstractType()->toString(), QString("Test< Dummy >"));
|
|
}
|
|
{
|
|
Declaration* decl = findDeclaration(top, QualifiedIdentifier("Test2<Dummy>::bm"), top->range().end);
|
|
QVERIFY(decl);
|
|
QVERIFY(decl->abstractType());
|
|
AbstractType::Ptr t(decl->abstractType());
|
|
QVERIFY(dynamic_cast<const IdentifiedType*>(t.unsafeData()));
|
|
QCOMPARE(decl->abstractType()->toString(), QString("Test< Dummy >"));
|
|
}
|
|
{
|
|
ClassDeclaration* decl = dynamic_cast<ClassDeclaration*>(findDeclaration(top, QualifiedIdentifier("Test2<Dummy>"), top->range().end));
|
|
QVERIFY(decl);
|
|
QVERIFY(decl->abstractType());
|
|
CppClassType::Ptr classType = decl->abstractType().cast<CppClassType>();
|
|
QVERIFY(classType);
|
|
QCOMPARE(decl->baseClassesSize(), 1u);
|
|
QVERIFY(decl->baseClasses()[0].baseClass);
|
|
CppClassType::Ptr parentClassType = decl->baseClasses()[0].baseClass.type<CppClassType>();
|
|
QVERIFY(parentClassType);
|
|
QCOMPARE(parentClassType->toString(), QString("Test< Dummy >"));
|
|
}
|
|
|
|
release(top);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void TestCppCodeCompletion::release(DUContext* top)
|
|
{
|
|
//EditorIntegrator::releaseTopRange(top->textRangePtr());
|
|
if(dynamic_cast<TopDUContext*>(top))
|
|
DUChain::self()->removeDocumentChain(static_cast<TopDUContext*>(top));
|
|
//delete top;
|
|
}
|
|
|
|
void TestCppCodeCompletion::addInclude( const QString& identity, const QString& text ) {
|
|
fakeIncludes[identity] = text;
|
|
}
|
|
|
|
//Only for debugging
|
|
QString print(const Cpp::ReferenceCountedStringSet& set) {
|
|
QString ret;
|
|
bool first = true;
|
|
Cpp::ReferenceCountedStringSet::Iterator it(set.iterator());
|
|
while(it) {
|
|
if(!first)
|
|
ret += ", ";
|
|
first = false;
|
|
|
|
ret += (*it).str();
|
|
++it;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
//Only for debugging
|
|
QStringList toStringList( const Cpp::ReferenceCountedStringSet& set ) {
|
|
QStringList ret;
|
|
Cpp::ReferenceCountedStringSet::Iterator it(set.iterator());
|
|
while(it) {
|
|
ret << (*it).str();
|
|
++it;
|
|
}
|
|
ret.sort();
|
|
return ret;
|
|
}
|
|
|
|
QStringList splitSorted(const QString& str) {
|
|
QStringList ret = str.split("\n");
|
|
ret.sort();
|
|
return ret;
|
|
}
|
|
|
|
void TestCppCodeCompletion::testEmptyMacroArguments() {
|
|
QString test("#define merge(prefix, suffix) prefix ## suffix\n void merge(test1, ) () { } void merge(, test2) () { }");
|
|
DUChainWriteLocker l(DUChain::lock());
|
|
TopDUContext* ctx = parse(test.toUtf8());
|
|
QCOMPARE(ctx->localDeclarations().count(), 2);
|
|
QCOMPARE(ctx->localDeclarations()[0]->identifier().toString(), QString("test1"));
|
|
QCOMPARE(ctx->localDeclarations()[1]->identifier().toString(), QString("test2"));
|
|
}
|
|
|
|
void TestCppCodeCompletion::testMacroExpansionRanges() {
|
|
{
|
|
QString test("#define TEST(X) int allabamma; \nTEST(C)\n");
|
|
DUChainWriteLocker l(DUChain::lock());
|
|
TopDUContext* ctx = parse(test.toUtf8());
|
|
QCOMPARE(ctx->localDeclarations().count(), 1);
|
|
kDebug() << ctx->localDeclarations()[0]->range().castToSimpleRange().textRange();
|
|
//kDebug() << ctx->localDeclarations()[1]->range().castToSimpleRange().textRange();
|
|
QCOMPARE(ctx->localDeclarations()[0]->range().castToSimpleRange().textRange(), KTextEditor::Range(1, 7, 1, 7)); //Because the macro TEST was expanded out of its physical range, the Declaration is collapsed.
|
|
// QCOMPARE(ctx->localDeclarations()[1]->range().castToSimpleRange().textRange(), KTextEditor::Range(1, 10, 1, 11));
|
|
//kDebug() << "Range:" << ctx->localDeclarations()[0]->range().castToSimpleRange().textRange();
|
|
}
|
|
{
|
|
QString test("#define A(X) bbbbbb\nint A(0);\n");
|
|
DUChainWriteLocker l(DUChain::lock());
|
|
TopDUContext* ctx = parse(test.toUtf8());
|
|
QCOMPARE(ctx->localDeclarations().count(), 1);
|
|
kDebug() << ctx->localDeclarations()[0]->range().castToSimpleRange().textRange();
|
|
QCOMPARE(ctx->localDeclarations()[0]->range().castToSimpleRange().textRange(), KTextEditor::Range(1, 8, 1, 8)); //Because the macro TEST was expanded out of its physical range, the Declaration is collapsed.
|
|
}
|
|
{
|
|
QString test("#define TEST namespace NS{int a;int b;int c;int d;int q;} class A{}; \nTEST; int a; int b; int c; int d;int e;int f;int g;int h;\n");
|
|
DUChainWriteLocker l(DUChain::lock());
|
|
TopDUContext* ctx = parse(test.toUtf8());
|
|
QCOMPARE(ctx->localDeclarations().count(), 10);
|
|
QCOMPARE(ctx->localDeclarations()[1]->range().castToSimpleRange().textRange(), KTextEditor::Range(1, 4, 1, 4)); //Because the macro TEST was expanded out of its physical range, the Declaration is collapsed.
|
|
QCOMPARE(ctx->localDeclarations()[2]->range().castToSimpleRange().textRange(), KTextEditor::Range(1, 10, 1, 11));
|
|
}
|
|
{
|
|
//The range of the merged declaration name should be trimmed to the end of the macro invocation
|
|
QString test("#define TEST(X) class X ## Class {};\nTEST(Hallo)\n");
|
|
DUChainWriteLocker l(DUChain::lock());
|
|
TopDUContext* ctx = parse(test.toUtf8());
|
|
QCOMPARE(ctx->localDeclarations().count(), 1);
|
|
QCOMPARE(ctx->localDeclarations()[0]->range().castToSimpleRange().textRange(), KTextEditor::Range(1, 5, 1, 11));
|
|
}
|
|
{
|
|
//The range of the merged declaration name should be within macro invocation even when the order of merging is different from the order of formal parameters
|
|
QString test("#define TEST(X, Y) class Y ## X {};\nTEST(Hello, World)\n");
|
|
DUChainWriteLocker l(DUChain::lock());
|
|
TopDUContext* ctx = parse(test.toUtf8());
|
|
QCOMPARE(ctx->localDeclarations().count(), 1);
|
|
QCOMPARE(ctx->localDeclarations()[0]->range().castToSimpleRange().textRange(), KTextEditor::Range(1, 12, 1, 18));
|
|
}
|
|
{
|
|
//The range of the merged declaration name should be collapsed if it does not start with a macro parameter
|
|
QString test("#define TEST(X) class Hallo ## X {};\nTEST(Class)\n");
|
|
DUChainWriteLocker l(DUChain::lock());
|
|
TopDUContext* ctx = parse(test.toUtf8());
|
|
QCOMPARE(ctx->localDeclarations().count(), 1);
|
|
QCOMPARE(ctx->localDeclarations()[0]->range().castToSimpleRange().textRange(), KTextEditor::Range(1, 11, 1, 11));
|
|
}
|
|
}
|
|
|
|
void TestCppCodeCompletion::testTimeMacro()
|
|
{
|
|
QString test("const char* str = __TIME__;");
|
|
DUChainWriteLocker l(DUChain::lock());
|
|
TopDUContext* ctx = parse(test.toUtf8());
|
|
QVERIFY(ctx->problems().isEmpty());
|
|
QCOMPARE(ctx->localDeclarations().count(), 1);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testDateMacro()
|
|
{
|
|
QString test("const char* str = __DATE__;");
|
|
DUChainWriteLocker l(DUChain::lock());
|
|
TopDUContext* ctx = parse(test.toUtf8());
|
|
QVERIFY(ctx->problems().isEmpty());
|
|
QCOMPARE(ctx->localDeclarations().count(), 1);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testFileMacro()
|
|
{
|
|
QString test("const char* str = __FILE__;");
|
|
DUChainWriteLocker l(DUChain::lock());
|
|
TopDUContext* ctx = parse(test.toUtf8());
|
|
QVERIFY(ctx->problems().isEmpty());
|
|
QCOMPARE(ctx->localDeclarations().count(), 1);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testNaiveMatching() {
|
|
return;
|
|
Cpp::EnvironmentManager::self()->setMatchingLevel(Cpp::EnvironmentManager::Naive);
|
|
{
|
|
addInclude("recursive_test_1.h", "#include \"recursive_test_2.h\"\nint i1;\n");
|
|
addInclude("recursive_test_2.h", "#include \"recursive_test_1.h\"\nint i2;\n");
|
|
|
|
TopDUContext* test1 = parse(QByteArray("#include \"recursive_test_1.h\"\n"), DumpNone);
|
|
DUChainWriteLocker l(DUChain::lock());
|
|
QCOMPARE(test1->recursiveImportIndices().count(), 3u);
|
|
QCOMPARE(test1->importedParentContexts().count(), 1);
|
|
QCOMPARE(test1->importedParentContexts()[0].indexedContext().context()->importedParentContexts().count(), 1);
|
|
QCOMPARE(test1->importedParentContexts()[0].indexedContext().context()->importedParentContexts()[0].indexedContext().context()->importedParentContexts().count(), 1);
|
|
QCOMPARE(test1->importedParentContexts()[0].indexedContext().context()->importedParentContexts()[0].indexedContext().context()->importedParentContexts()[0].indexedContext().context()->importedParentContexts().count(), 1);
|
|
Cpp::EnvironmentFile* envFile1 = dynamic_cast<Cpp::EnvironmentFile*>(test1->parsingEnvironmentFile().data());
|
|
QVERIFY(envFile1);
|
|
QVERIFY(envFile1->headerGuard().isEmpty());
|
|
release(test1);
|
|
}
|
|
|
|
}
|
|
|
|
void TestCppCodeCompletion::testHeaderGuards() {
|
|
{
|
|
TopDUContext* test1 = parse(QByteArray("#ifndef GUARD\n#define GUARD\nint x = 5; \n#endif\n#define BLA\n"), DumpNone);
|
|
DUChainWriteLocker l(DUChain::lock());
|
|
Cpp::EnvironmentFile* envFile1 = dynamic_cast<Cpp::EnvironmentFile*>(test1->parsingEnvironmentFile().data());
|
|
QVERIFY(envFile1);
|
|
QVERIFY(envFile1->headerGuard().isEmpty());
|
|
release(test1);
|
|
}
|
|
{
|
|
TopDUContext* test1 = parse(QByteArray("#ifndef GUARD\n#define GUARD\nint x = 5;\n#ifndef GUARD\n#define GUARD\n#endif\n#if defined(TEST)\n int q = 4;#endif\n#endif\n"), DumpNone);
|
|
DUChainWriteLocker l(DUChain::lock());
|
|
Cpp::EnvironmentFile* envFile1 = dynamic_cast<Cpp::EnvironmentFile*>(test1->parsingEnvironmentFile().data());
|
|
QVERIFY(envFile1);
|
|
QCOMPARE(envFile1->headerGuard().str(), QString("GUARD"));
|
|
release(test1);
|
|
}
|
|
{
|
|
TopDUContext* test1 = parse(QByteArray("int x;\n#ifndef GUARD\n#define GUARD\nint x = 5; \n#endif\n"), DumpNone);
|
|
DUChainWriteLocker l(DUChain::lock());
|
|
Cpp::EnvironmentFile* envFile1 = dynamic_cast<Cpp::EnvironmentFile*>(test1->parsingEnvironmentFile().data());
|
|
QVERIFY(envFile1);
|
|
QVERIFY(envFile1->headerGuard().isEmpty());
|
|
release(test1);
|
|
}
|
|
{
|
|
TopDUContext* test1 = parse(QByteArray("#define X\n#ifndef GUARD\n#define GUARD\nint x = 5; \n#endif\n"), DumpNone);
|
|
DUChainWriteLocker l(DUChain::lock());
|
|
Cpp::EnvironmentFile* envFile1 = dynamic_cast<Cpp::EnvironmentFile*>(test1->parsingEnvironmentFile().data());
|
|
QVERIFY(envFile1);
|
|
QVERIFY(envFile1->headerGuard().isEmpty());
|
|
release(test1);
|
|
}
|
|
{
|
|
TopDUContext* test1 = parse(QByteArray("#ifndef GUARD\n#define GUARD\nint x = 5; \n#endif\nint o;\n"), DumpNone);
|
|
DUChainWriteLocker l(DUChain::lock());
|
|
Cpp::EnvironmentFile* envFile1 = dynamic_cast<Cpp::EnvironmentFile*>(test1->parsingEnvironmentFile().data());
|
|
QVERIFY(envFile1);
|
|
QVERIFY(envFile1->headerGuard().isEmpty());
|
|
release(test1);
|
|
}
|
|
}
|
|
|
|
void TestCppCodeCompletion::testEnvironmentMatching() {
|
|
{
|
|
CppPreprocessEnvironment::setRecordOnlyImportantString(false);
|
|
|
|
addInclude("deep2.h", "#ifdef WANT_DEEP\nint x;\n#undef WANT_DEEP\n#endif\n");
|
|
addInclude("deep1.h", "#define WANT_DEEP\n#include \"deep2.h\"\n");
|
|
TopDUContext* test1 = parse(QByteArray("#include \"deep1.h\""), DumpNone);
|
|
Cpp::EnvironmentFile* envFile1 = dynamic_cast<Cpp::EnvironmentFile*>(test1->parsingEnvironmentFile().data());
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QVERIFY(envFile1);
|
|
QCOMPARE(envFile1->definedMacroNames().set().count(), 0u);
|
|
QCOMPARE(envFile1->definedMacros().set().count(), 0u);
|
|
QCOMPARE(envFile1->usedMacros().set().count(), 0u);
|
|
}
|
|
|
|
addInclude("h1.h", "#ifndef H1_H \n#define H1_H \n class H1 {};\n #else \n class H1_Already_Defined {}; \n#endif");
|
|
addInclude("h1_user.h", "#ifndef H1_USER \n#define H1_USER \n#include \"h1.h\" \nclass H1User {}; \n#endif\n");
|
|
|
|
{
|
|
TopDUContext* test1 = parse(QByteArray("#include \"h1.h\" \n#include \"h1_user.h\"\n\nclass Honk {};"), DumpNone);
|
|
//We test here, whether the environment-manager re-combines h1_user.h so it actually contains a definition of class H1.
|
|
//In the version parsed in test1, H1_H was already defined, so the h1.h parsed into h1_user.h was parsed to contain H1_Already_Defined.
|
|
TopDUContext* test2 = parse(QByteArray("#include \"h1_user.h\"\n"), DumpNone);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QVERIFY(test1->parsingEnvironmentFile());
|
|
QVERIFY(test2->parsingEnvironmentFile());
|
|
Cpp::EnvironmentFile* envFile1 = dynamic_cast<Cpp::EnvironmentFile*>(test1->parsingEnvironmentFile().data());
|
|
Cpp::EnvironmentFile* envFile2 = dynamic_cast<Cpp::EnvironmentFile*>(test2->parsingEnvironmentFile().data());
|
|
QVERIFY(envFile1);
|
|
QVERIFY(envFile2);
|
|
|
|
QCOMPARE(envFile1->usedMacros().set().count(), 0u);
|
|
QCOMPARE(envFile2->usedMacros().set().count(), 0u);
|
|
QVERIFY(findDeclaration( test1, Identifier("H1") ));
|
|
|
|
QCOMPARE( envFile1->contentStartLine(), 3 );
|
|
}
|
|
|
|
{ //Test shadowing of strings through #undefs
|
|
addInclude("stringset_test1.h", "String1 s1;\n#undef String2\n String2 s2;");
|
|
addInclude("stringset_test2.h", "String1 s1;\n#undef String2\n String2 s2;");
|
|
|
|
{
|
|
TopDUContext* top = parse(QByteArray("#include \"stringset_test1.h\"\n"), DumpNone);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QVERIFY(top->parsingEnvironmentFile());
|
|
Cpp::EnvironmentFile* envFile = dynamic_cast<Cpp::EnvironmentFile*>(top->parsingEnvironmentFile().data());
|
|
QVERIFY(envFile);
|
|
kDebug() << "url" << envFile->url().str();
|
|
QCOMPARE(envFile->usedMacros().set().count(), 0u);
|
|
QCOMPARE(toStringList(envFile->strings()), splitSorted("String1\ns1\ns2")); //The #undef protects String2, so it cannot be affected from outside
|
|
}
|
|
{
|
|
TopDUContext* top = parse(QByteArray("#define String1\n#include \"stringset_test1.h\"\nString2 String1;"), DumpNone); //Both String1 and String2 are shadowed. String1 by the macro, and String2 by the #undef in stringset_test1.h
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QVERIFY(top->parsingEnvironmentFile());
|
|
Cpp::EnvironmentFile* envFile = dynamic_cast<Cpp::EnvironmentFile*>(top->parsingEnvironmentFile().data());
|
|
QVERIFY(envFile);
|
|
//String1 is shadowed by the macro-definition, so it is not a string that can be affected from outside.
|
|
QCOMPARE(toStringList(envFile->strings()), splitSorted("s1\ns2"));
|
|
QCOMPARE(toStringList(envFile->usedMacroNames()), QStringList()); //No macros from outside were used
|
|
|
|
QCOMPARE(envFile->definedMacros().set().count(), 1u);
|
|
QCOMPARE(envFile->usedMacros().set().count(), 0u);
|
|
|
|
QCOMPARE(top->importedParentContexts().count(), 1);
|
|
TopDUContext* top2 = dynamic_cast<TopDUContext*>(top->importedParentContexts()[0].context(0));
|
|
QVERIFY(top2);
|
|
Cpp::EnvironmentFile* envFile2 = dynamic_cast<Cpp::EnvironmentFile*>(top2->parsingEnvironmentFile().data());
|
|
QVERIFY(envFile2);
|
|
|
|
QCOMPARE(envFile2->definedMacros().set().count(), 0u);
|
|
|
|
QCOMPARE(toStringList(envFile2->usedMacroNames()), QStringList("String1")); //stringset_test1.h used the Macro String1 from outside
|
|
QCOMPARE(toStringList(envFile2->strings()), splitSorted("String1\ns1\ns2"));
|
|
}
|
|
{
|
|
TopDUContext* top = parse(QByteArray("#define String1\n#undef String1\n#include \"stringset_test1.h\""), DumpNone);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QVERIFY(top->parsingEnvironmentFile());
|
|
Cpp::EnvironmentFile* envFile = dynamic_cast<Cpp::EnvironmentFile*>(top->parsingEnvironmentFile().data());
|
|
QVERIFY(envFile);
|
|
QCOMPARE(envFile->definedMacros().set().count(), 0u);
|
|
QCOMPARE(envFile->usedMacros().set().count(), 0u);
|
|
//String1 is shadowed by the macro-definition, so it is not a string that can be affected from outside.
|
|
kDebug() << toStringList(envFile->strings()) << splitSorted("s1\ns2");
|
|
QCOMPARE(toStringList(envFile->strings()), splitSorted("s1\ns2"));
|
|
QCOMPARE(toStringList(envFile->usedMacroNames()), QStringList()); //No macros from outside were used
|
|
|
|
QCOMPARE(top->importedParentContexts().count(), 1);
|
|
TopDUContext* top2 = dynamic_cast<TopDUContext*>(top->importedParentContexts()[0].context(0));
|
|
QVERIFY(top2);
|
|
Cpp::EnvironmentFile* envFile2 = dynamic_cast<Cpp::EnvironmentFile*>(top2->parsingEnvironmentFile().data());
|
|
QVERIFY(envFile2);
|
|
QCOMPARE(envFile2->definedMacros().set().count(), 0u);
|
|
|
|
QCOMPARE(toStringList(envFile2->usedMacroNames()), QStringList()); //stringset_test1.h used the Macro String1 from outside. However it is an undef-macro, so it does not appear in usedMacroNames() and usedMacros()
|
|
QCOMPARE(envFile2->usedMacros().set().count(), (unsigned int)0);
|
|
QCOMPARE(toStringList(envFile2->strings()), splitSorted("String1\ns1\ns2"));
|
|
}
|
|
{
|
|
addInclude("usingtest1.h", "#define HONK\nMACRO m\n#undef HONK2\n");
|
|
|
|
TopDUContext* top = parse(QByteArray("#define MACRO meh\nint MACRO;\n#include \"usingtest1.h\"\n"), DumpNone);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QVERIFY(top->parsingEnvironmentFile());
|
|
Cpp::EnvironmentFile* envFile = dynamic_cast<Cpp::EnvironmentFile*>(top->parsingEnvironmentFile().data());
|
|
QVERIFY(envFile);
|
|
QCOMPARE(envFile->definedMacros().set().count(), 2u);
|
|
QCOMPARE(envFile->unDefinedMacroNames().set().count(), 1u);
|
|
QCOMPARE(envFile->usedMacros().set().count(), 0u);
|
|
QCOMPARE(envFile->usedMacroNames().set().count(), 0u);
|
|
|
|
kDebug() << toStringList(envFile->strings()) ;
|
|
QCOMPARE(envFile->strings().count(), 3u); //meh, m, int
|
|
|
|
QCOMPARE(top->importedParentContexts().count(), 1);
|
|
TopDUContext* top2 = dynamic_cast<TopDUContext*>(top->importedParentContexts()[0].context(0));
|
|
QVERIFY(top2);
|
|
Cpp::EnvironmentFile* envFile2 = dynamic_cast<Cpp::EnvironmentFile*>(top2->parsingEnvironmentFile().data());
|
|
QVERIFY(envFile2);
|
|
QCOMPARE(envFile2->definedMacros().set().count(), 1u);
|
|
QCOMPARE(envFile2->unDefinedMacroNames().set().count(), 1u);
|
|
QCOMPARE(envFile2->usedMacros().set().count(), 1u);
|
|
QCOMPARE(envFile2->usedMacroNames().set().count(), 1u);
|
|
kDebug() << toStringList(envFile2->strings()) ;
|
|
QCOMPARE(envFile2->strings().count(), 3u); //meh(from macro), MACRO, m
|
|
}
|
|
}
|
|
|
|
/* addInclude( "envmatch_header1.h", "#include \"envmatch_header2.h\"\n class SomeName; #define SomeName SomeAlternativeName" );
|
|
addInclude( "envmatch_header2.h", "#ifndef SOMEDEF\n #define SOMEDEF\n#endif\n" );
|
|
QByteArray method1("#include \"envmatch_header1.h\"");
|
|
QByteArray method2("#include \"envmatch_header1.h\"");
|
|
QByteArray method3("#include \"envmatch_header1.h\"\n#include \"envmatch_header1.h\"");
|
|
|
|
DUContext* top1 = parse(method1, DumpNone);
|
|
DUContext* top2 = parse(method1, DumpNone);
|
|
DUContext* top3 = parse(method1, DumpNone);
|
|
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
|
|
QCOMPARE(top1->importedParentContexts().count(), 1);
|
|
QCOMPARE(top2->importedParentContexts().count(), 1);
|
|
// QCOMPARE(top3->importedParentContexts().count(), 2);
|
|
|
|
QCOMPARE(top1->importedParentContexts()[0], top2->importedParentContexts()[1]);*/
|
|
}
|
|
|
|
void TestCppCodeCompletion::testPreprocessor() {
|
|
TEST_FILE_PARSE_ONLY
|
|
|
|
IncludeFileList includes;
|
|
|
|
{
|
|
QString a = "#define Q(c) c; char* q = #c; \n Q(int i;\n char* c = \"a\";)\n";
|
|
QString preprocessed = preprocess(IndexedString(), a, includes);
|
|
kDebug() << "preprocessed:" << preprocessed;
|
|
QVERIFY(preprocessed.contains("\"int i;\\n char* c = \\\"a\\\";")); //The newline must have been escaped correctly, and the string as well
|
|
TopDUContext* top = parse(a.toLocal8Bit(), DumpNone);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
kDebug() << top->localDeclarations()[0]->identifier().toString();
|
|
QCOMPARE(top->localDeclarations().count(), 3);
|
|
QCOMPARE(top->localDeclarations()[0]->range().start.line, 1);
|
|
QCOMPARE(top->localDeclarations()[1]->range().start.line, 2);
|
|
QCOMPARE(top->localDeclarations()[2]->range().start.line, 2);
|
|
}
|
|
{
|
|
QString a = "#undef __attribute__\n__attribute__((visibility(\"default\")))";
|
|
QString preprocessed = preprocess(IndexedString(), a, includes);
|
|
kDebug() << "preprocessed:" << preprocessed;
|
|
QVERIFY(!preprocessed.contains ("__attribute__"));
|
|
}
|
|
{
|
|
QString a = "#ifdef __attribute__\npassed\n#else\nfailed\n#endif";
|
|
QString preprocessed = preprocess(IndexedString(), a, includes);
|
|
kDebug() << "preprocessed: " << preprocessed;
|
|
QVERIFY(!preprocessed.contains("failed"));
|
|
QVERIFY(preprocessed.contains("passed"));
|
|
}
|
|
{
|
|
QString a = "#define Q(c) c ## ULL \n void test() {int i = Q(0x5);}";
|
|
QString preprocessed = preprocess(IndexedString(), a, includes);
|
|
kDebug() << "preprocessed:" << preprocessed;
|
|
TopDUContext* top = parse(a.toLocal8Bit(), DumpNone);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(top->childContexts().count(), 2);
|
|
QCOMPARE(top->childContexts()[1]->localDeclarations().count(), 1);
|
|
}
|
|
{
|
|
QString a = "#define MA(x) T<x> a\n #define MB(x) T<x>\n #define MC(X) int\n #define MD(X) c\n template <typename P1> struct A {}; template <typename P2> struct T {}; int main(int argc, char ** argv) { MA(A<int>); A<MB(int)> b; MC(a)MD(b); MC(a)d; }";
|
|
QString preprocessed = preprocess(IndexedString(), a, includes);
|
|
kDebug() << "preprocessed:" << preprocessed;
|
|
TopDUContext* top = parse(a.toUtf8(), DumpAll);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(top->childContexts().count(), 6);
|
|
QCOMPARE(top->childContexts()[5]->localDeclarations().count(), 4);
|
|
}
|
|
#ifdef TEST_MACRO_EXPANSION_ORDER
|
|
//Not working yet
|
|
{//No macro-expansion should happen on the first layer of a macro-call
|
|
QString preprocessed = preprocess(IndexedString(), "#define VAL_KIND A \n#define DO_CAT_I(a, b) a ## b \n#define DO_CAT(a, b) DO_CAT_I(a, b) \nint DO_CAT(Value_, VAL_KIND); \nint DO_CAT_I(Value_, VAL_KIND);\n int VAL_KIND;\nint DO_CAT(VAL_KIND, _Value);\nint DO_CAT(VAL_KIND, _Value);\n", includes);
|
|
kDebug() << preprocessed;
|
|
TopDUContext* top = parse(preprocessed.toUtf8(), DumpNone);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(top->localDeclarations().count(), 5);
|
|
QCOMPARE(top->localDeclarations()[0]->identifier(), Identifier("Value_A"));
|
|
QCOMPARE(top->localDeclarations()[1]->identifier(), Identifier("Value_VAL_KIND"));
|
|
QCOMPARE(top->localDeclarations()[1]->identifier(), Identifier("A"));
|
|
QCOMPARE(top->localDeclarations()[0]->identifier(), Identifier("A_Value"));
|
|
QCOMPARE(top->localDeclarations()[0]->identifier(), Identifier("VAL_KIND_Value"));
|
|
}
|
|
#endif
|
|
{//Test macro redirection
|
|
QString test = preprocess(IndexedString(), "#define M1(X) X ## _m1 \n#define M2(X) M ## X\n#define M3 M2\n#define M4 M3 \nM4(1)(hallo)", includes);
|
|
kDebug() << test;
|
|
QCOMPARE(test.trimmed(), QString("hallo_m1"));
|
|
}
|
|
{//Test replacement of merged preprocessor function calls
|
|
TopDUContext* top = parse(QByteArray("#define MACRO_1(X) X ## _fromMacro1 \n#define A(pred, n) MACRO_ ## n(pred) \n#define D(X,Y) A(X ## Y, 1) \nint D(a,ba);"), DumpNone);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(top->localDeclarations().count(), 1);
|
|
QCOMPARE(top->localDeclarations()[0]->identifier(), Identifier("aba_fromMacro1"));
|
|
}
|
|
{//Test merging
|
|
TopDUContext* top = parse(QByteArray("#define D(X,Y) X ## Y \nint D(a,ba);"), DumpNone);
|
|
IncludeFileList includes;
|
|
kDebug() << preprocess(IndexedString("somefile"), "#define D(X,Y) X ## Y \nint D(a,ba);", includes);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(top->localDeclarations().count(), 1);
|
|
QCOMPARE(top->localDeclarations()[0]->identifier(), Identifier("aba"));
|
|
}
|
|
{
|
|
TopDUContext* top = parse(QByteArray("#define MERGE(a, b) a ## b \n#define MERGE_WITH_PARENS(par) MERGE ## par \nint MERGE_WITH_PARENS((int, B));"), DumpNone);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(top->localDeclarations().count(), 1);
|
|
QCOMPARE(top->localDeclarations()[0]->identifier(), Identifier("intB"));
|
|
}
|
|
{//Test simple #if
|
|
TopDUContext* top = parse(QByteArray("#define X\n#if defined(X)\nint xDefined;\n#endif\n#if !defined(X)\nint xNotDefined;\n#endif\n#if (!defined(X))\nint xNotDefined2;\n#endif"), DumpNone);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(top->localDeclarations().count(), 1);
|
|
QCOMPARE(top->localDeclarations()[0]->identifier(), Identifier("xDefined"));
|
|
}
|
|
{//Test simple #if
|
|
TopDUContext* top = parse(QByteArray("#if defined(X)\nint xDefined;\n#endif\n#if !defined(X)\nint xNotDefined;\n#endif\n#if (!defined(X))\nint xNotDefined2;\n#endif"), DumpNone);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QVERIFY(top->localDeclarations().count() >= 1);
|
|
QCOMPARE(top->localDeclarations()[0]->identifier(), Identifier("xNotDefined"));
|
|
QCOMPARE(top->localDeclarations().count(), 2);
|
|
QCOMPARE(top->localDeclarations()[1]->identifier(), Identifier("xNotDefined2"));
|
|
}
|
|
{//Test multi-line definitions
|
|
TopDUContext* top = parse(QByteArray("#define X \\\nint i;\\\nint o;\nX;\n"), DumpNone);
|
|
IncludeFileList includes;
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(top->localDeclarations().count(), 2);
|
|
}
|
|
{//Test multi-line definitions
|
|
TopDUContext* top = parse(QByteArray("#define THROUGH_DEFINE(X) X\nclass B {\nclass C{\n};\nC* cPcPcPcPcPcPcPcPcP;\n};\nB* bP;\nvoid test() {\nTHROUGH_DEFINE(bP->cPcPcPcPcPcPcPcPcP);\n}\n"), DumpNone);
|
|
IncludeFileList includes;
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(top->childContexts().count(), 3);
|
|
QCOMPARE(top->childContexts()[0]->localDeclarations().count(), 2);
|
|
QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().size(), 1);
|
|
QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().begin()->count(), 1);
|
|
QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().begin()->at(0).start.column, 19);
|
|
QCOMPARE(top->childContexts()[0]->localDeclarations()[1]->uses().begin()->at(0).end.column, 37);
|
|
}
|
|
{//Test merging
|
|
TopDUContext* top = parse(QByteArray("#define D(X,Y) X ## Y \nint D(a,ba);"), DumpNone);
|
|
IncludeFileList includes;
|
|
kDebug() << preprocess(IndexedString("somefile"), "#define D(X,Y) X ## Y \nint D(a,ba);", includes);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(top->localDeclarations().count(), 1);
|
|
QCOMPARE(top->localDeclarations()[0]->identifier(), Identifier("aba"));
|
|
}
|
|
{//Test merging
|
|
TopDUContext* top = parse(QByteArray("#define A(x) int x;\n#define B(name) A(bo ## name)\nB(Hallo)"), DumpNone);
|
|
IncludeFileList includes;
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(top->localDeclarations().count(), 1);
|
|
QCOMPARE(top->localDeclarations()[0]->identifier(), Identifier("boHallo"));
|
|
}
|
|
{//Test __builtin_offsetof
|
|
TopDUContext* top = parse(QByteArray("typedef struct a { int i; } t_a; int o = __builtin_offsetof(t_a, i);"), DumpNone);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(top->localDeclarations().count(), 3);
|
|
}
|
|
{//Test __builtin_offsetof with a struct type (make sure spaces are handled correctly by the macro)
|
|
TopDUContext* top = parse(QByteArray("struct a { int i; }; int o = __builtin_offsetof(struct a, i);"), DumpNone);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(top->localDeclarations().count(), 2);
|
|
}
|
|
{
|
|
QString parsed = preprocess(IndexedString("somefile"),
|
|
"#define NC(...) __VA_ARGS__\nNC(bla,bla)\n", includes)
|
|
.replace(QRegExp("[\n\t ]+"), "");
|
|
QEXPECT_FAIL("", "Variadic macros unsupported", Continue);
|
|
QCOMPARE(parsed, QString("bla,bla"));
|
|
// fallback: at least don't fail parsing altogether, see https://bugs.kde.org/show_bug.cgi?id=308556
|
|
QCOMPARE(parsed, QString());
|
|
|
|
parsed = preprocess(IndexedString("somefile"),
|
|
"#define PUT_BETWEEN(x,y) x y x\n#define NC(...) __VA_ARGS__\nPUT_BETWEEN(NC(pair<a,b>), c)\n", includes)
|
|
.replace(QRegExp("[\n\t ]+"), " ").trimmed();
|
|
QEXPECT_FAIL("", "Variadic macros unsupported", Continue);
|
|
QCOMPARE(parsed, QString("pair<a,b> c pair<a,b>"));
|
|
|
|
}
|
|
}
|
|
|
|
void TestCppCodeCompletion::testArgumentList()
|
|
{
|
|
QMap<QByteArray, QString> codeToArgList;
|
|
codeToArgList.insert("void foo(int arg[]){}", "(int arg[])");
|
|
codeToArgList.insert("void foo(int arg[1]){}", "(int arg[1])");
|
|
codeToArgList.insert("void foo(int arg[][1]){}", "(int arg[][1])");
|
|
codeToArgList.insert("void foo(int arg[1][1]){}", "(int arg[1][1])");
|
|
codeToArgList.insert("void foo(int arg[][1][1]){}", "(int arg[][1][1])");
|
|
codeToArgList.insert("void foo(void){}", "(void)");
|
|
codeToArgList.insert("void foo(int){}", "(int)");
|
|
QMap< QByteArray, QString >::const_iterator it = codeToArgList.constBegin();
|
|
while (it != codeToArgList.constEnd()){
|
|
qDebug() << "input function is:" << it.key();
|
|
TopDUContext* top = parse(it.key(), DumpNone);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QCOMPARE(top->localDeclarations().size(), 1);
|
|
|
|
CompletionItemTester complCtx(top, "");
|
|
Cpp::NormalDeclarationCompletionItem item(DeclarationPointer(top->localDeclarations().first()), KSharedPtr<KDevelop::CodeCompletionContext>(complCtx.completionContext.data()));
|
|
QString ret;
|
|
Cpp::createArgumentList(item, ret, 0);
|
|
QCOMPARE(ret, it.value());
|
|
|
|
release(top);
|
|
|
|
++it;
|
|
}
|
|
}
|
|
|
|
void TestCppCodeCompletion::testStaticMethods()
|
|
{
|
|
QByteArray code("struct A {\n"
|
|
" public: static void myPublicStatic() {}\n"
|
|
" protected: static void myProtectedStatic() {}\n"
|
|
" private: static void myPrivateStatic() {}\n"
|
|
" public: void myPublicNonStatic() {}\n"
|
|
" protected: void myProtectedNonStatic() {}\n"
|
|
" private: void myPrivateNonStatic() {}\n"
|
|
"};\n"
|
|
"A a; int main() { return 0;}");
|
|
TopDUContext* top = parse(code, DumpNone);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QVERIFY(top->problems().isEmpty());
|
|
QCOMPARE(top->childContexts().size(), 3);
|
|
{
|
|
// in body of main
|
|
CompletionItemTester complCtx(top->childContexts().last(), "a.");
|
|
QVERIFY(complCtx.completionContext->isValid());
|
|
QCOMPARE(complCtx.names, QStringList() << "myPublicStatic" << "myPublicNonStatic");
|
|
}
|
|
{
|
|
// in body of main
|
|
CompletionItemTester complCtx(top->childContexts().last(), "A::");
|
|
QVERIFY(complCtx.completionContext->isValid());
|
|
QEXPECT_FAIL("", "non-static functions don't get filtered. comment in context.cpp: ///@todo what NOT to show on static member choose? Actually we cannot hide all non-static functions, because of function-pointers", Continue);
|
|
QCOMPARE(complCtx.names, QStringList() << "myPublicStatic"); // fails and gets skipped
|
|
// this is a fallback to verify the current behavior
|
|
QCOMPARE(complCtx.names, QStringList() << "myPublicStatic" << "myPublicNonStatic");
|
|
}
|
|
{
|
|
// in body of myPrivate
|
|
CompletionItemTester complCtx(top->childContexts().first()->childContexts().last(), "this->");
|
|
QVERIFY(complCtx.completionContext->isValid());
|
|
QCOMPARE(complCtx.names, QStringList() << "myPublicStatic" << "myProtectedStatic" << "myPrivateStatic" << "myPublicNonStatic" << "myProtectedNonStatic" << "myPrivateNonStatic");
|
|
}
|
|
{
|
|
// in body of myPrivate
|
|
CompletionItemTester complCtx(top->childContexts().first()->childContexts().last(), "A::");
|
|
QVERIFY(complCtx.completionContext->isValid());
|
|
QEXPECT_FAIL("", "non-static functions don't get filtered. comment in context.cpp: ///@todo what NOT to show on static member choose? Actually we cannot hide all non-static functions, because of function-pointers", Continue);
|
|
QCOMPARE(complCtx.names, QStringList() << "myPublicStatic" << "myProtectedStatic" << "myPrivateStatic"); // fails and gets skipped
|
|
// this is a fallback to verify the current behavior
|
|
QCOMPARE(complCtx.names, QStringList() << "myPublicStatic" << "myProtectedStatic" << "myPrivateStatic" << "myPublicNonStatic" << "myProtectedNonStatic" << "myPrivateNonStatic");
|
|
}
|
|
release(top);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testStringInComment_data()
|
|
{
|
|
QTest::addColumn<QString>("prefix");
|
|
|
|
QTest::newRow("cpp comment") << QString("/* \" */\n");
|
|
QTest::newRow("c comment") << QString("// \"\n");
|
|
}
|
|
|
|
void TestCppCodeCompletion::testStringInComment()
|
|
{
|
|
QByteArray code("");
|
|
TopDUContext* top = parse(code, DumpNone);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
QVERIFY(top->problems().isEmpty());
|
|
|
|
QFETCH(QString, prefix);
|
|
|
|
CompletionItemTester complCtx(top, prefix);
|
|
QVERIFY(complCtx.completionContext->isValid());
|
|
}
|
|
|
|
void TestCppCodeCompletion::testProperties()
|
|
{
|
|
QByteArray code(
|
|
"class A{\n"
|
|
" __qt_property__(int myProp READ myProp WRITE setMyProp);\n"
|
|
"public:\n"
|
|
" int myProp() const;\n" // actual getter
|
|
" void setMyProp(int);\n" // actual setter
|
|
"};\n"
|
|
" A aStack; A* aHeap = new A; int main() {}");
|
|
TopDUContext* top = parse(code, DumpNone);
|
|
DUChainWriteLocker lock;
|
|
QVERIFY(top->problems().isEmpty());
|
|
QVector<Declaration*> declarations = top->childContexts().first()->localDeclarations();
|
|
QCOMPARE(declarations.count(), 2);
|
|
Declaration* getter = declarations.first();
|
|
QVERIFY(getter->isFunctionDeclaration());
|
|
Declaration* setter = declarations.last();
|
|
QVERIFY(getter->isFunctionDeclaration());
|
|
|
|
DUContext* ctx = top->childContexts().last();
|
|
QCOMPARE(ctx->type(), DUContext::Other);
|
|
|
|
{
|
|
CompletionItemTester complCtx(ctx, "aStack.");
|
|
QVERIFY(complCtx.completionContext->isValid());
|
|
QCOMPARE(complCtx.completionContext->accessType(), Cpp::CodeCompletionContext::MemberAccess);
|
|
QCOMPARE(complCtx.completionContext->onlyShow(), Cpp::CodeCompletionContext::ShowAll);
|
|
QVERIFY(complCtx.containsDeclaration(getter));
|
|
QVERIFY(complCtx.containsDeclaration(setter));
|
|
}
|
|
{
|
|
CompletionItemTester complCtx(ctx, "aHeap->");
|
|
QVERIFY(complCtx.completionContext->isValid());
|
|
QCOMPARE(complCtx.completionContext->accessType(), Cpp::CodeCompletionContext::ArrowMemberAccess);
|
|
QCOMPARE(complCtx.completionContext->onlyShow(), Cpp::CodeCompletionContext::ShowAll);
|
|
QVERIFY(complCtx.containsDeclaration(getter));
|
|
QVERIFY(complCtx.containsDeclaration(setter));
|
|
}
|
|
|
|
release(top);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testAnonStruct()
|
|
{
|
|
QByteArray code("void foo() { struct { int a; } myStruct; }");
|
|
TopDUContext* top = parse(code, DumpNone);
|
|
DUChainWriteLocker lock;
|
|
QVERIFY(top->problems().isEmpty());
|
|
|
|
CompletionItemTester complCtx(top->childContexts().last(), "");
|
|
QVERIFY(!complCtx.names.contains("<unknown>"));
|
|
|
|
release(top);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testOverrideCtor()
|
|
{
|
|
QByteArray code("class A { A(int) {} }; class B : public A { B(float) {} }; class C : public B { C(float) {} };");
|
|
TopDUContext* top = parse(code, DumpNone);
|
|
DUChainWriteLocker lock;
|
|
QVERIFY(top->problems().isEmpty());
|
|
|
|
Declaration* aCtor = top->childContexts().at(0)->localDeclarations().first();
|
|
Declaration* bCtor = top->childContexts().at(1)->localDeclarations().first();
|
|
|
|
{
|
|
// in B we should see A's ctor
|
|
CompletionItemTester complCtx(top->childContexts().at(1), "");
|
|
QVERIFY(complCtx.containsDeclaration(aCtor));
|
|
QVERIFY(!complCtx.containsDeclaration(bCtor));
|
|
}
|
|
{
|
|
// in C we should _only_ see B's ctor. Since it's already overridden, nothing should be shown.
|
|
CompletionItemTester complCtx(top->childContexts().at(2), "");
|
|
QVERIFY(!complCtx.containsDeclaration(aCtor));
|
|
QVERIFY(!complCtx.containsDeclaration(bCtor));
|
|
}
|
|
|
|
release(top);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testFilterVoid()
|
|
{
|
|
QByteArray code("struct S{}; void foo() {}");
|
|
TopDUContext* top = parse(code, DumpNone);
|
|
DUChainWriteLocker lock;
|
|
QVERIFY(top->problems().isEmpty());
|
|
|
|
// filter void here
|
|
foreach(const QString& s, QStringList() << "int i = " << "S* sPtr = ") {
|
|
CompletionItemTester complCtx(top->childContexts().last(), s);
|
|
QVERIFY(!complCtx.containsDeclaration(top->localDeclarations().last()));
|
|
}
|
|
|
|
// but not here
|
|
CompletionItemTester complCtx(top->childContexts().last(), "void (*fPtr)() =");
|
|
QVERIFY(complCtx.containsDeclaration(top->localDeclarations().last()));
|
|
|
|
release(top);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testExecuteKeepWord_data()
|
|
{
|
|
QTest::addColumn<QString>("code");
|
|
QTest::addColumn<QString>("expectedCode");
|
|
|
|
QTest::newRow("replaceWord") << "\nstruct Foo { };\nFoo f;\nfbar();"
|
|
<< "f();";
|
|
|
|
QTest::newRow("structfunction") << "\nstruct Foo { int bar() { return 0; } };\nFoo f;\nfbar();"
|
|
<< "f.bar();";
|
|
|
|
QTest::newRow("pointer") << "\nstruct Foo { int bar() { return 0; } };\nFoo *f;\nfbar();"
|
|
<< "f->bar();";
|
|
|
|
QTest::newRow("reference") << "\nstruct Foo { int bar() { return 0; } };\nFoo &f;\nfbar();"
|
|
<< "f.bar();";
|
|
|
|
QTest::newRow("smartPtr") << "template<class T> struct SmartPointer { T* operator->() {return 0;}};\nstruct Foo { int bar() { return 0; } };\nSmartPointer<Foo> f;\nfbar();"
|
|
<< "f->bar();";
|
|
|
|
QTest::newRow("enum") << "\nclass f { enum Foo { xy, xz }; };\n\nfFoo x;"
|
|
<< "f::Foo x;";
|
|
|
|
QTest::newRow("staticfunction") << "struct f { static void bar() {} };\n\n\nfbar();"
|
|
<< "f::bar();";
|
|
}
|
|
|
|
void TestCppCodeCompletion::testExecuteKeepWord()
|
|
{
|
|
QFETCH(QString, code);
|
|
TopDUContext* top = parse(code.toAscii(), DumpAll);
|
|
|
|
KTextEditor::Editor* editor = KTextEditor::EditorChooser::editor();
|
|
QVERIFY(editor);
|
|
|
|
KTextEditor::Document* doc = editor->createDocument(this);
|
|
QVERIFY(doc);
|
|
doc->setText(code);
|
|
doc->startEditing();
|
|
|
|
KTextEditor::View *v = doc->createView(0);
|
|
v->setCursorPosition(KTextEditor::Cursor(3, 1));
|
|
|
|
DUChainWriteLocker lock;
|
|
QVERIFY(top->problems().isEmpty());
|
|
|
|
CompletionItemTester complCtx(top->childContexts().last(), "");
|
|
KSharedPtr<CompletionTreeItem> item;
|
|
for(int i=0; i<complCtx.items.length(); ++i) {
|
|
kDebug() << complCtx.itemData(i).toString();
|
|
if (complCtx.itemData(i).toString()=="f") {
|
|
item = complCtx.items.at(i);
|
|
}
|
|
}
|
|
QVERIFY(!item.isNull());
|
|
item->execute(doc, KTextEditor::Range(3, 0, 3, 4));
|
|
|
|
QFETCH(QString, expectedCode);
|
|
QCOMPARE(doc->line(3), expectedCode);
|
|
|
|
doc->endEditing();
|
|
|
|
release(top);
|
|
|
|
delete v;
|
|
delete doc;
|
|
}
|
|
|
|
|
|
//BEGIN: Helper
|
|
|
|
class TestPreprocessor : public rpp::Preprocessor
|
|
{
|
|
public:
|
|
|
|
TestCppCodeCompletion* cc;
|
|
IncludeFileList& included;
|
|
rpp::pp* pp;
|
|
bool stopAfterHeaders;
|
|
Cpp::EnvironmentFilePointer environmentFile;
|
|
|
|
TestPreprocessor( TestCppCodeCompletion* _cc, IncludeFileList& _included, bool _stopAfterHeaders ) : cc(_cc), included(_included), pp(0), stopAfterHeaders(_stopAfterHeaders) {
|
|
}
|
|
|
|
rpp::Stream* sourceNeeded(QString& fileName, rpp::Preprocessor::IncludeType /*type*/, int sourceLine, bool /*skipCurrentPath*/)
|
|
{
|
|
QMap<QString,QString>::const_iterator it = cc->fakeIncludes.constFind(fileName);
|
|
if( it != cc->fakeIncludes.constEnd() || !pp ) {
|
|
kDebug(9007) << "parsing included file \"" << fileName << "\"";
|
|
included << LineContextPair( dynamic_cast<TopDUContext*>(cc->parse( (*it).toUtf8(), TestCppCodeCompletion::DumpNone, pp, KUrl(it.key()))), sourceLine );
|
|
} else {
|
|
kDebug(9007) << "could not find include-file \"" << fileName << "\"";
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void setPp( rpp::pp* _pp ) {
|
|
pp = _pp;
|
|
}
|
|
|
|
virtual void headerSectionEnded(rpp::Stream& stream) {
|
|
if( environmentFile )
|
|
environmentFile->setContentStartLine( stream.originalInputPosition().line );
|
|
if(stopAfterHeaders)
|
|
stream.toEnd();
|
|
}
|
|
|
|
virtual void foundHeaderGuard(rpp::Stream& /*stream*/, KDevelop::IndexedString guardName) {
|
|
environmentFile->setHeaderGuard(guardName);
|
|
}
|
|
};
|
|
|
|
QString TestCppCodeCompletion::preprocess( const IndexedString& url, const QString& text, IncludeFileList& included, rpp::pp* parent, bool stopAfterHeaders, KSharedPtr<Cpp::EnvironmentFile>* paramEnvironmentFile, rpp::LocationTable** returnLocationTable, PreprocessedContents* targetContents ) {
|
|
TestPreprocessor ppc( this, included, stopAfterHeaders );
|
|
|
|
|
|
rpp::pp preprocessor(&ppc);
|
|
ppc.setPp( &preprocessor );
|
|
|
|
KSharedPtr<Cpp::EnvironmentFile> environmentFile;
|
|
if( paramEnvironmentFile && *paramEnvironmentFile )
|
|
environmentFile = *paramEnvironmentFile;
|
|
else
|
|
environmentFile = Cpp::EnvironmentFilePointer( new Cpp::EnvironmentFile( IndexedString(url.str()), 0 ) );
|
|
|
|
ppc.environmentFile = environmentFile;
|
|
|
|
if( paramEnvironmentFile )
|
|
*paramEnvironmentFile = environmentFile;
|
|
|
|
CppPreprocessEnvironment* currentEnvironment = new CppPreprocessEnvironment( environmentFile );
|
|
preprocessor.setEnvironment( currentEnvironment );
|
|
currentEnvironment->setEnvironmentFile( environmentFile );
|
|
|
|
if( parent )
|
|
preprocessor.environment()->swapMacros(parent->environment());
|
|
else
|
|
currentEnvironment->merge(CppUtils::standardMacros());
|
|
|
|
PreprocessedContents contents = preprocessor.processFile(url.str(), text.toUtf8());
|
|
if(targetContents)
|
|
*targetContents = contents;
|
|
|
|
QString result = QString::fromUtf8(stringFromContents(contents));
|
|
|
|
if (returnLocationTable)
|
|
*returnLocationTable = preprocessor.environment()->takeLocationTable();
|
|
|
|
currentEnvironment->finishEnvironment();
|
|
|
|
if( parent ) {
|
|
preprocessor.environment()->swapMacros(parent->environment());
|
|
static_cast<CppPreprocessEnvironment*>(parent->environment())->environmentFile()->merge(*environmentFile);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
TopDUContext* TestCppCodeCompletion::parse(const QByteArray& unit, DumpAreas dump, rpp::pp* parent, KUrl _identity, TopDUContext* update)
|
|
{
|
|
if (dump)
|
|
kDebug(9007) << "==== Beginning new test case...:" << endl << unit;
|
|
|
|
ParseSession* session = new ParseSession();
|
|
;
|
|
|
|
static int testNumber = 0;
|
|
IndexedString url(QString("file:///internal/%1").arg(testNumber++));
|
|
if( !_identity.isEmpty() )
|
|
url = IndexedString(_identity);
|
|
|
|
IncludeFileList included;
|
|
QList<DUContext*> temporaryIncluded;
|
|
|
|
rpp::LocationTable* locationTable;
|
|
|
|
Cpp::EnvironmentFilePointer file;
|
|
|
|
PreprocessedContents contents;
|
|
|
|
preprocess( url, QString::fromUtf8(unit), included, parent, false, &file, &locationTable, &contents );
|
|
|
|
session->setContents( contents, locationTable );
|
|
|
|
if( parent ) {
|
|
//Temporarily insert all files parsed previously by the parent, so forward-declarations can be resolved etc.
|
|
TestPreprocessor* testPreproc = dynamic_cast<TestPreprocessor*>(parent->preprocessor());
|
|
if( testPreproc ) {
|
|
foreach( LineContextPair include, testPreproc->included ) {
|
|
if( !containsContext( included, include.context ) ) {
|
|
included.push_front( include );
|
|
temporaryIncluded << include.context;
|
|
}
|
|
}
|
|
} else {
|
|
kDebug(9007) << "PROBLEM";
|
|
}
|
|
}
|
|
|
|
Parser parser(&control);
|
|
TranslationUnitAST* ast = parser.parse(session);
|
|
ast->session = session;
|
|
|
|
if (dump & DumpAST) {
|
|
kDebug(9007) << "===== AST:";
|
|
cppDumper.dump(ast, session);
|
|
}
|
|
|
|
DeclarationBuilder definitionBuilder(session);
|
|
|
|
TopDUContext* top = definitionBuilder.buildDeclarations(file, ast, &included, ReferencedTopDUContext(update));
|
|
|
|
UseBuilder useBuilder(session);
|
|
useBuilder.buildUses(ast);
|
|
foreach(KDevelop::ProblemPointer problem, useBuilder.problems()) {
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
top->addProblem(problem);
|
|
}
|
|
|
|
if (dump & DumpDUChain) {
|
|
kDebug(9007) << "===== DUChain:";
|
|
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
KDevelop::dumpDUContext(top);
|
|
}
|
|
|
|
if( parent ) {
|
|
//Remove temporarily inserted files parsed previously by the parent
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
TestPreprocessor* testPreproc = dynamic_cast<TestPreprocessor*>(parent->preprocessor());
|
|
if( testPreproc ) {
|
|
foreach( DUContext* context, temporaryIncluded )
|
|
top->removeImportedParentContext( context );
|
|
} else {
|
|
kDebug(9007) << "PROBLEM";
|
|
}
|
|
}
|
|
|
|
|
|
if (dump)
|
|
kDebug(9007) << "===== Finished test case.";
|
|
|
|
delete session;
|
|
|
|
return top;
|
|
}
|
|
|
|
void TestCppCodeCompletion::testCompletedIncludeFilePath()
|
|
{
|
|
KTempDir tempDir;
|
|
QDir dir(tempDir.name());
|
|
QString innerDirName = "directoryabcde";
|
|
dir.mkdir(innerDirName);
|
|
QDir innerDir(tempDir.name() + innerDirName);
|
|
QString filename = "xxxxx.h";
|
|
QFile file(innerDir.absoluteFilePath(filename));
|
|
QVERIFY(file.open(QIODevice::ReadWrite));
|
|
QList<IncludeItem> includeItems = CppUtils::allFilesInIncludePath(QString(tempDir.name() + "source.cpp"), true, innerDirName, QStringList() << tempDir.name());
|
|
QCOMPARE(includeItems.size(), 1);
|
|
QCOMPARE(includeItems[0].basePath, KUrl(innerDir.absolutePath()));
|
|
}
|
|
|
|
/**
|
|
* Check that there are no multiple include completion items referring to the same file
|
|
*/
|
|
void TestCppCodeCompletion::testMultipleIncludeCompletionItems()
|
|
{
|
|
KTempDir tempDir;
|
|
QDir dir(tempDir.name());
|
|
QString innerDirName1 = "directory1abcde";
|
|
dir.mkdir(innerDirName1);
|
|
QString innerDirName2 = "directory2abcde";
|
|
dir.mkdir(innerDirName2);
|
|
QDir innerDir1(tempDir.name() + innerDirName1);
|
|
QString filename = "xxxxx.h";
|
|
QFile file(innerDir1.absoluteFilePath(filename));
|
|
QVERIFY(file.open(QIODevice::ReadWrite));
|
|
QList<IncludeItem> includeItems = CppUtils::allFilesInIncludePath(innerDir1.absoluteFilePath("source.cpp"), true, QString("../" + innerDirName1), QStringList() << QString(tempDir.name() + innerDirName1) << QString(tempDir.name() + innerDirName2));
|
|
QCOMPARE(includeItems.size(), 1);
|
|
QCOMPARE(includeItems[0].basePath, KUrl(innerDir1.absolutePath()));
|
|
}
|
|
|
|
void TestCppCodeCompletion::testAfterVisibility_data()
|
|
{
|
|
QTest:: addColumn<QString>("vis");
|
|
QTest::newRow("public") << "public:";
|
|
QTest::newRow("protected") << "public:";
|
|
QTest::newRow("private") << "public:";
|
|
// happens e.g. after Q_OBJECT
|
|
QTest::newRow("private-private") << "private:private:";
|
|
QTest::newRow("private-public") << "private:public:";
|
|
QTest::newRow("private-protected") << "private:protected:";
|
|
}
|
|
|
|
void TestCppCodeCompletion::testAfterVisibility()
|
|
{
|
|
QByteArray code("struct b { virtual void foo(); }; struct c : public b {};");
|
|
TopDUContext* top = parse(code, DumpNone);
|
|
DUChainWriteLocker lock;
|
|
QVERIFY(top->problems().isEmpty());
|
|
|
|
QFETCH(QString, vis);
|
|
CompletionItemTester complCtx(top->childContexts().last(), vis);
|
|
QVERIFY(complCtx.completionContext->isValid());
|
|
QVERIFY(complCtx.containsDeclaration(top->findDeclarations(QualifiedIdentifier("b::foo")).first()));
|
|
|
|
release(top);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testNoQuadrupleColon()
|
|
{
|
|
QByteArray code("namespace Foobar { static int var; }\n main() { Foobar::var; }");
|
|
TopDUContext* top = parse(code, DumpNone);
|
|
DUChainWriteLocker lock;
|
|
QVERIFY(top->problems().isEmpty());
|
|
|
|
CompletionItemTester tester(top->childContexts().last());
|
|
QVERIFY(tester.completionContext->isValid());
|
|
KSharedPtr<CompletionTreeItem> item;
|
|
for( int i = 0; i < tester.items.length(); ++i ) {
|
|
if( tester.itemData( i ).toString() == "Foobar" ) {
|
|
item = tester.items.at( i );
|
|
}
|
|
}
|
|
QVERIFY( !item.isNull() );
|
|
|
|
KTextEditor::Editor* editor = KTextEditor::EditorChooser::editor();
|
|
QVERIFY(editor);
|
|
KTextEditor::Document* doc = editor->createDocument(this);
|
|
QVERIFY(doc);
|
|
|
|
// verify it adds the "::" when the doc is empty
|
|
doc->setText("");
|
|
KTextEditor::View* v = doc->createView(0);
|
|
|
|
doc->startEditing();
|
|
KTextEditor::Cursor c( 0, 0 );
|
|
v->setCursorPosition( c );
|
|
|
|
item->execute( doc, Range( c, 0 ) );
|
|
QCOMPARE( doc->line( 0 ), QString("Foobar::") );
|
|
|
|
// verify it doesn't when there's already a "::"
|
|
doc->setText("::var;");
|
|
v->setCursorPosition( c );
|
|
|
|
item->execute( doc, Range( c, 0 ) );
|
|
QCOMPARE( doc->line( 0 ), QString("Foobar::var;") );
|
|
|
|
doc->endEditing();
|
|
}
|
|
|
|
void TestCppCodeCompletion::testLookaheadMatches_data()
|
|
{
|
|
QTest::addColumn<QString>("insert"); // inserted code
|
|
QTest::addColumn<QStringList>("completions"); // completions offered
|
|
QStringList all;
|
|
all << "m_one" << "m_two" << "m_smartOne" << "m_access" << "ThreeTwoOne" << "One"
|
|
<< "Two" << "OneSmartPointer" << "Access" << "OneTwoThree" << "this";
|
|
|
|
QTest::newRow("Function Arg") << "m_smartOne.setOne("
|
|
<< (QStringList() << "m_two.hatPointer" << "m_smartOne.operator->" << "setOne" << all);
|
|
QTest::newRow("Smart Pointer") << "int foo = "
|
|
<< (QStringList() << "int =" << "m_one.alsoRan" << "m_two.meToo" << "m_smartOne->alsoRan" << "m_access.publicMember" << all);
|
|
QTest::newRow("Type Conversions") << "m_smartOne = "
|
|
<< (QStringList() << "OneSmartPointer m_smartOne =" << "m_two.hatPointer" << all );
|
|
QTest::newRow("Assignment") << "m_one = "
|
|
<< (QStringList() << "One m_one =" << "m_two.hat" << all );
|
|
QTest::newRow("Equality") << "m_one == "
|
|
<< (QStringList() << "m_two.hat" << all );
|
|
QTest::newRow("ReturnAccess") << "return"
|
|
<< (QStringList() << "return int" << "m_one.alsoRan" << "m_two.meToo" << "m_smartOne->alsoRan" << "m_access.publicMember" << all );
|
|
QTest::newRow("No Lookahead") << "One::NoLookahead test = "
|
|
<< (QStringList() << "One::NoLookahead NoLookahead =" << "NO" << "CAN" << "SEE" << all );
|
|
}
|
|
|
|
void TestCppCodeCompletion::testLookaheadMatches()
|
|
{
|
|
QByteArray test = "struct One { enum NoLookahead { NO, CAN, SEE, }; int alsoRan; typedef int myInt; };"
|
|
"struct Two { One hat; One *hatPointer; int meToo(); };"
|
|
"struct OneSmartPointer { OneSmartPointer(One*) {}; void setOne(One*); One* operator->() const {} };"
|
|
"class Access{ int privateMember; public: int publicMember; };"
|
|
"struct OneTwoThree { One m_one; Two m_two; OneSmartPointer m_smartOne; Access m_access; int ThreeTwoOne() { } };";
|
|
QFETCH(QString, insert);
|
|
QFETCH(QStringList, completions);
|
|
TopDUContext* top = parse(test, DumpNone);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
DUContext *testContext = top->childContexts()[4]->childContexts()[1];
|
|
CompletionItemTester tester(testContext, insert);
|
|
qDebug() << tester.names << "VERSUS" << completions;
|
|
QCOMPARE(tester.names.toSet(), completions.toSet());
|
|
release(top);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testMemberAccessInstance()
|
|
{
|
|
QByteArray test = "struct foo{}; int main() {}";
|
|
TopDUContext* top = parse(test, DumpNone);
|
|
DUChainWriteLocker lock(DUChain::lock());
|
|
CompletionItemTester tester(top->childContexts()[2], "foo.");
|
|
QCOMPARE(tester.names, QStringList());
|
|
release(top);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testNestedInlineNamespace()
|
|
{
|
|
QByteArray test = "namespace a { inline namespace b { void foo(); } } int main() {}";
|
|
TopDUContext* top = parse(test, DumpAll);
|
|
DUChainWriteLocker lock;
|
|
CompletionItemTester tester(top->childContexts()[2], "a::");
|
|
QCOMPARE(tester.names, QStringList() << "b" << "foo" );
|
|
release(top);
|
|
}
|
|
|
|
void TestCppCodeCompletion::testDuplicatedNamespace()
|
|
{
|
|
// see also: https://bugs.kde.org/show_bug.cgi?id=328803
|
|
QByteArray test( "namespace foo { const int bar = 1; }\n"
|
|
"namespace foo { const int asdf = 1; }\n"
|
|
"namespace foo { }\n"
|
|
"int main() {}\n" );
|
|
TopDUContext* top = parse(test, DumpNone);
|
|
DUChainWriteLocker lock;
|
|
CompletionItemTester tester(top->childContexts().last(), "foo::");
|
|
//TODO: the sort-order is apparently undefined...
|
|
qSort(tester.names);
|
|
QCOMPARE(tester.names, QStringList() << "asdf" << "bar" );
|
|
release(top);
|
|
}
|
|
|
|
#include "test_cppcodecompletion.moc"
|