File indexing completed on 2024-05-12 04:39:16

0001 /*
0002     SPDX-FileCopyrightText: 2014 David Stevens <dgedstevens@gmail.com>
0003     SPDX-FileCopyrightText: 2014 Kevin Funk <kfunk@kde.org>
0004     SPDX-FileCopyrightText: 2015 Milian Wolff <mail@milianw.de>
0005     SPDX-FileCopyrightText: 2015 Sergey Kalinichev <kalinichev.so.0@gmail.com>
0006 
0007     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0008 */
0009 
0010 #include "test_codecompletion.h"
0011 #include <language/backgroundparser/backgroundparser.h>
0012 
0013 #include <tests/testcore.h>
0014 #include <tests/autotestshell.h>
0015 #include <tests/testfile.h>
0016 #include <tests/testproject.h>
0017 
0018 #include "duchain/parsesession.h"
0019 #include "duchain/clanghelpers.h"
0020 #include "util/clangtypes.h"
0021 
0022 #include <interfaces/idocumentcontroller.h>
0023 #include <interfaces/ilanguagecontroller.h>
0024 
0025 #include <language/codecompletion/codecompletiontesthelper.h>
0026 #include <language/duchain/types/functiontype.h>
0027 
0028 #include "codecompletion/completionhelper.h"
0029 #include "codecompletion/context.h"
0030 #include "codecompletion/includepathcompletioncontext.h"
0031 #include "../clangsettings/clangsettingsmanager.h"
0032 
0033 #include "sanitizer_test_init.h"
0034 
0035 #include <KTextEditor/Editor>
0036 #include <KTextEditor/Document>
0037 #include <KTextEditor/View>
0038 
0039 #include <KConfigGroup>
0040 
0041 #include <QVersionNumber>
0042 #include <QStandardPaths>
0043 
0044 #include <optional>
0045 
0046 int main(int argc, char** argv)
0047 {
0048     KDevelop::sanitizerTestInit(argv);
0049     QTEST_MAIN_IMPL(TestCodeCompletion)
0050 }
0051 
0052 static const auto NoMacroOrBuiltin = ClangCodeCompletionContext::ContextFilters(
0053     ClangCodeCompletionContext::NoBuiltins | ClangCodeCompletionContext::NoMacros);
0054 
0055 using namespace KDevelop;
0056 
0057 using ClangCodeCompletionItemTester = CodeCompletionItemTester<ClangCodeCompletionContext>;
0058 
0059 struct CompletionItems {
0060     CompletionItems(){}
0061     CompletionItems(const KTextEditor::Cursor& position, const QStringList& completions,
0062                     const QStringList& declarationItems = {}, const std::optional<QString>& code = std::nullopt)
0063         : position(position)
0064         , completions(completions)
0065         , declarationItems(declarationItems)
0066         , code(code){};
0067     KTextEditor::Cursor position;
0068     QStringList completions;
0069     QStringList declarationItems; ///< completion items that have associated declarations. Declarations with higher match quality at the top. @sa KTextEditor::CodeCompletionModel::MatchQuality
0070     std::optional<QString> code; ///< If we expect the completion (not) to change the code, e.g., arrow-to-dot
0071 };
0072 Q_DECLARE_TYPEINFO(CompletionItems, Q_MOVABLE_TYPE);
0073 Q_DECLARE_METATYPE(CompletionItems)
0074 
0075 
0076 struct CompletionPriorityItem
0077 {
0078     CompletionPriorityItem(const QString& name, int matchQuality = 0, int inheritanceDepth = 0, const QString& failMessage = {})
0079         : name(name)
0080         , failMessage(failMessage)
0081         , matchQuality(matchQuality)
0082         , inheritanceDepth(inheritanceDepth)
0083     {}
0084 
0085     QString name;
0086     QString failMessage;
0087     int matchQuality;
0088     int inheritanceDepth;
0089 };
0090 
0091 struct CompletionPriorityItems : public CompletionItems
0092 {
0093     CompletionPriorityItems(){}
0094     CompletionPriorityItems(const KTextEditor::Cursor& position, const QList<CompletionPriorityItem>& completions)
0095         : CompletionItems(position, {})
0096         , completions(completions)
0097     {};
0098 
0099     QList<CompletionPriorityItem> completions;
0100 };
0101 
0102 Q_DECLARE_TYPEINFO(CompletionPriorityItems, Q_MOVABLE_TYPE);
0103 Q_DECLARE_METATYPE(CompletionPriorityItems)
0104 
0105 namespace {
0106 
0107 struct NoopTestFunction
0108 {
0109     void operator()(const ClangCodeCompletionItemTester& /*tester*/) const
0110     {
0111     }
0112 };
0113 
0114 QString textForDocument(const QUrl& url, const KTextEditor::Cursor& position)
0115 {
0116     bool close = false;
0117 
0118     auto* doc = ICore::self()->documentController()->documentForUrl(url);
0119     if (!doc) {
0120         doc = ICore::self()->documentController()->openDocument(url);
0121         close = true;
0122     }
0123 
0124     auto text = doc->textDocument()->text({{0, 0}, position});
0125 
0126     if (close) {
0127         doc->close(IDocument::Discard);
0128     }
0129 
0130     return text;
0131 }
0132 
0133 QExplicitlySharedDataPointer<ClangCodeCompletionContext> createContext(const ReferencedTopDUContext& top,
0134                                                                        const ParseSessionData::Ptr& sessionData,
0135                                                                        const KTextEditor::Cursor position,
0136                                                                        const QString& code = {})
0137 {
0138     const auto url = top->url().toUrl();
0139     const auto text = code.isEmpty() ? textForDocument(url, position) : code;
0140     return QExplicitlySharedDataPointer<ClangCodeCompletionContext>{
0141         new ClangCodeCompletionContext(DUContextPointer(top), sessionData, url, position, text)};
0142 }
0143 
0144 template<typename CustomTestFunction = NoopTestFunction>
0145 void executeCompletionTest(const ReferencedTopDUContext& top, const CompletionItems& expectedCompletionItems,
0146                            const ClangCodeCompletionContext::ContextFilters& filters = NoMacroOrBuiltin,
0147                            CustomTestFunction customTestFunction = {})
0148 {
0149     DUChainReadLocker lock;
0150     QVERIFY(top);
0151     QVERIFY(top->ast());
0152     const ParseSessionData::Ptr sessionData(dynamic_cast<ParseSessionData*>(top->ast().data()));
0153     QVERIFY(sessionData);
0154     lock.unlock();
0155     // TODO: We should not need to pass 'session' to the context, should just use the base class ctor
0156     auto context = createContext(top, sessionData, expectedCompletionItems.position);
0157     context->setFilters(filters);
0158     lock.lock();
0159 
0160     auto tester = ClangCodeCompletionItemTester(context);
0161 
0162     int previousMatchQuality = 10;
0163     for(const auto& declarationName : expectedCompletionItems.declarationItems){
0164         const auto declarationItem = tester.findItem(declarationName);
0165         QVERIFY(declarationItem);
0166         QVERIFY(declarationItem->declaration());
0167 
0168         auto matchQuality = tester.itemData(declarationItem, KTextEditor::CodeCompletionModel::Name, KTextEditor::CodeCompletionModel::MatchQuality).toInt();
0169         QVERIFY(matchQuality <= previousMatchQuality);
0170         previousMatchQuality = matchQuality;
0171     }
0172 
0173     tester.names.sort();
0174     QEXPECT_FAIL("look-ahead function primary type argument", "No API in LibClang to determine expected code completion type", Continue);
0175     QEXPECT_FAIL("look-ahead template parameter substitution", "No parameters substitution so far", Continue);
0176 #if CINDEX_VERSION_MINOR < 30
0177     QEXPECT_FAIL("look-ahead auto item", "Auto type, like many other types, is not exposed through LibClang. We assign DelayedType to it instead of IdentifiedType", Continue);
0178 #endif
0179     if (QTest::currentTestFunction() == QByteArrayLiteral("testImplementAfterEdit") && expectedCompletionItems.position.line() == 3) {
0180         QEXPECT_FAIL("", "TU is not properly updated after edit", Continue);
0181     }
0182     if (QTest::currentTestFunction() == QByteArrayLiteral("testClangCodeCompletion")) {
0183         QEXPECT_FAIL("look-ahead pointer", "self-assignment isn't done anymore, so we don't find any suitable type anymore", Continue);
0184 
0185         if (QVersionNumber::fromString(ClangHelpers::clangVersion()) >= QVersionNumber(9, 0, 0)) {
0186             QEXPECT_FAIL("enum-case", "quite a lot of unrelated cruft is suggested, needs to be fixed upstream", Continue);
0187         }
0188     }
0189     if (tester.names.size() != expectedCompletionItems.completions.size()) {
0190         qDebug() << "different results:\nactual:" << tester.names << "\nexpected:" << expectedCompletionItems.completions;
0191     }
0192     QCOMPARE(tester.names, expectedCompletionItems.completions);
0193 
0194     lock.unlock(); // customTestFunction should lock appropriately
0195     customTestFunction(tester);
0196 }
0197 
0198 template<typename CustomTestFunction = NoopTestFunction>
0199 void executeCompletionTest(const QString& code, const CompletionItems& expectedCompletionItems,
0200                            const ClangCodeCompletionContext::ContextFilters& filters = NoMacroOrBuiltin,
0201                            CustomTestFunction customTestFunction = {},
0202                            // Using QStringLiteral fails here with Ubuntu Bionic's GNU 7.4.0 C++ compiler,
0203                            // Assembler messages:  Error: symbol `_ZZNK12_GLOBAL__N_1UlvE_clEvE15qstring_literal' is already defined
0204                            // Seems the symbol is not including the template arguments, but might be created
0205                            // per instantiation of the template
0206                            // Seems fixed in newer versions of GCC
0207                            const QString& fileExtension = QString::fromLatin1("cpp"))
0208 {
0209     TestFile file(code, fileExtension);
0210     QVERIFY(file.parseAndWait(TopDUContext::AllDeclarationsContextsUsesAndAST));
0211     executeCompletionTest(file.topContext(), expectedCompletionItems, filters, customTestFunction);
0212 }
0213 
0214 void executeCompletionPriorityTest(const QString& code, const CompletionPriorityItems& expectedCompletionItems,
0215                            const ClangCodeCompletionContext::ContextFilters& filters = NoMacroOrBuiltin)
0216 {
0217     TestFile file(code, QStringLiteral("cpp"));
0218     QVERIFY(file.parseAndWait(TopDUContext::AllDeclarationsContextsUsesAndAST));
0219     DUChainReadLocker lock;
0220     auto top = file.topContext();
0221     QVERIFY(top);
0222     const ParseSessionData::Ptr sessionData(dynamic_cast<ParseSessionData*>(top->ast().data()));
0223     QVERIFY(sessionData);
0224 
0225     // don't hold DUChain lock when constructing ClangCodeCompletionContext
0226     lock.unlock();
0227 
0228     auto context = createContext(top, sessionData, expectedCompletionItems.position);
0229     context->setFilters(filters);
0230 
0231     lock.lock();
0232     auto tester = ClangCodeCompletionItemTester(context);
0233 
0234     for(const auto& declaration : expectedCompletionItems.completions){
0235         qDebug() << "verifying declaration:" << declaration.name;
0236 
0237         const auto declarationItem = tester.findItem(declaration.name);
0238         if (!declarationItem || !declarationItem->declaration()) {
0239             qDebug() << "names of all completion-items:" << tester.names;
0240         }
0241         QVERIFY(declarationItem);
0242         QVERIFY(declarationItem->declaration());
0243 
0244         auto matchQuality = tester.itemData(declarationItem, KTextEditor::CodeCompletionModel::Name, KTextEditor::CodeCompletionModel::MatchQuality).toInt();
0245         auto inheritanceDepth = declarationItem->inheritanceDepth();
0246 
0247         if (!declaration.failMessage.isEmpty()) {
0248             QEXPECT_FAIL("", declaration.failMessage.toUtf8().constData(), Continue);
0249         }
0250         QVERIFY2(matchQuality == declaration.matchQuality && inheritanceDepth == declaration.inheritanceDepth,
0251                  qPrintable(QStringLiteral("%1 == %2 && %3 == %4")
0252                                 .arg(matchQuality)
0253                                 .arg(declaration.matchQuality)
0254                                 .arg(inheritanceDepth)
0255                                 .arg(declaration.inheritanceDepth)));
0256     }
0257 }
0258 
0259 void executeMemberAccessReplacerTest(const QString& code, const CompletionItems& expectedCompletionItems,
0260                                      const ClangCodeCompletionContext::ContextFilters& filters = NoMacroOrBuiltin)
0261 {
0262     TestFile file(code, QStringLiteral("cpp"));
0263 
0264     auto document = ICore::self()->documentController()->openDocument(file.url().toUrl());
0265     QVERIFY(document);
0266     ICore::self()->documentController()->activateDocument(document);
0267     auto view = ICore::self()->documentController()->activeTextDocumentView();
0268     Q_ASSERT(view);
0269     view->setCursorPosition(expectedCompletionItems.position);
0270 
0271     QVERIFY(file.parseAndWait(TopDUContext::AllDeclarationsContextsUsesAndAST));
0272     DUChainReadLocker lock;
0273     auto top = file.topContext();
0274     QVERIFY(top);
0275     const ParseSessionData::Ptr sessionData(dynamic_cast<ParseSessionData*>(top->ast().data()));
0276     QVERIFY(sessionData);
0277 
0278     lock.unlock();
0279 
0280     auto context = createContext(top, sessionData, expectedCompletionItems.position, code);
0281 
0282     QApplication::processEvents();
0283 
0284     if (expectedCompletionItems.code) {
0285         QCOMPARE(document->textDocument()->text(), *expectedCompletionItems.code);
0286     }
0287 
0288     document->close(KDevelop::IDocument::Silent);
0289 
0290     // The previous ClangCodeCompletionContext call should replace member access.
0291     // That triggers an update request in the duchain which we are not interested in,
0292     // so let's stop that request.
0293     ICore::self()->languageController()->backgroundParser()->removeDocument(file.url());
0294 
0295     context = createContext(top, sessionData, expectedCompletionItems.position);
0296     context->setFilters(filters);
0297     lock.lock();
0298     auto tester = ClangCodeCompletionItemTester(context);
0299 
0300     tester.names.sort();
0301     QCOMPARE(tester.names, expectedCompletionItems.completions);
0302 }
0303 
0304 using IncludeTester = CodeCompletionItemTester<IncludePathCompletionContext>;
0305 
0306 QExplicitlySharedDataPointer<IncludePathCompletionContext> executeIncludePathCompletion(TestFile* file, const KTextEditor::Cursor& position)
0307 {
0308     if (!file->parseAndWait(TopDUContext::AllDeclarationsContextsUsesAndAST)) {
0309         QTest::qFail("Failed to parse source file.", __FILE__, __LINE__);
0310         return {};
0311     }
0312 
0313     DUChainReadLocker lock;
0314     auto top = file->topContext();
0315     if (!top) {
0316         QTest::qFail("Failed to parse source file.", __FILE__, __LINE__);
0317         return {};
0318     }
0319     const ParseSessionData::Ptr sessionData(dynamic_cast<ParseSessionData*>(top->ast().data()));
0320     if (!sessionData) {
0321         QTest::qFail("Failed to acquire parse session data.", __FILE__, __LINE__);
0322         return {};
0323     }
0324 
0325     DUContextPointer topPtr(top);
0326 
0327     lock.unlock();
0328 
0329     auto text = file->fileContents();
0330     int textLength = -1;
0331     if (position.isValid()) {
0332         textLength = 0;
0333         for (int i = 0; i < position.line(); ++i) {
0334             textLength = text.indexOf('\n', textLength) + 1;
0335         }
0336         textLength += position.column();
0337     }
0338     auto context = new IncludePathCompletionContext(topPtr, sessionData, file->url().toUrl(), position, text.mid(0, textLength));
0339     return QExplicitlySharedDataPointer<IncludePathCompletionContext>{context};
0340 }
0341 
0342 constexpr const char* definesAndIncludesConfigName = "CustomDefinesAndIncludes";
0343 
0344 }
0345 
0346 void TestCodeCompletion::cleanup()
0347 {
0348     KSharedConfig::openConfig()->deleteGroup(definesAndIncludesConfigName);
0349 }
0350 
0351 void TestCodeCompletion::testClangCodeCompletion()
0352 {
0353     QFETCH(QString, code);
0354     QFETCH(CompletionItems, expectedItems);
0355 
0356     executeCompletionTest(code, expectedItems);
0357 }
0358 
0359 void TestCodeCompletion::testClangCodeCompletion_data()
0360 {
0361     QTest::addColumn<QString>("code");
0362     QTest::addColumn<CompletionItems>("expectedItems");
0363 
0364     QTest::newRow("assignment")
0365         << "int foo = 5; \nint bar = "
0366         << CompletionItems{{1,9}, {
0367             "foo",
0368         }, {"foo"}};
0369     QTest::newRow("dotmemberaccess")
0370         << "class Foo { public: void foo() {} bool operator=(Foo &&) }; int main() { Foo f; \nf. "
0371         << CompletionItems{{1, 2}, {
0372             "foo",
0373             "operator="
0374         }, {"foo",  "operator="}};
0375     QTest::newRow("arrowmemberaccess")
0376         << "class Foo { public: void foo() {} }; int main() { Foo* f = new Foo; \nf-> }"
0377         << CompletionItems{{1, 3}, {
0378             "foo"
0379         }, {"foo"}};
0380     QTest::newRow("enum-case")
0381         << "enum Foo { foo, bar }; int main() { Foo f; switch (f) {\ncase "
0382         << CompletionItems{{1,4}, {
0383             "bar",
0384             "foo",
0385         }, {"foo", "bar"}};
0386     QTest::newRow("only-private")
0387         << "class SomeStruct { private: void priv() {} };\n"
0388            "int main() { SomeStruct s;\ns. "
0389         << CompletionItems{{2, 2}, {
0390         }};
0391     QTest::newRow("private-friend")
0392         << "class SomeStruct { private: void priv() {} friend int main(); };\n"
0393            "int main() { SomeStruct s;\ns. "
0394         << CompletionItems{{2, 2}, {
0395             "priv",
0396         }, {"priv"}};
0397     QTest::newRow("private-public")
0398         << "class SomeStruct { public: void pub() {} private: void priv() {} };\n"
0399            "int main() { SomeStruct s;\ns. "
0400         << CompletionItems{{2, 2}, {
0401             "pub",
0402         }, {"pub"}};
0403     QTest::newRow("protected-public")
0404         << "class SomeStruct { public: void pub() {} protected: void prot() {} };\n"
0405            "int main() { SomeStruct s;\ns. "
0406         << CompletionItems{{2, 2}, {
0407             "pub",
0408         }, {"pub"}};
0409     QTest::newRow("localVariable")
0410         << "int main() { int localVariable;\nloc "
0411         << CompletionItems{{1, 3},
0412             {"localVariable","main"},
0413             {"localVariable", "main"}
0414         };
0415     QTest::newRow("globalVariable")
0416         << "int globalVariable;\nint main() { \ngl "
0417         << CompletionItems{{2, 2},
0418             {"globalVariable","main"},
0419             {"globalVariable", "main"}
0420         };
0421     QTest::newRow("namespaceVariable")
0422         << "namespace NameSpace{int variable};\nint main() { \nNameSpace:: "
0423         << CompletionItems{{2, 11},
0424             {"variable"},
0425             {"variable"}
0426         };
0427     QTest::newRow("parentVariable")
0428         << "class A{public: int m_variable;};class B : public A{};\nint main() { B b;\nb. "
0429         << CompletionItems{{2, 2},
0430             {"m_variable"},
0431             {"m_variable"}
0432         };
0433     QTest::newRow("itemsPriority")
0434         << "class A; class B; void f(A); int main(){ A c; B b;f(\n} "
0435         << CompletionItems{{1, 0},
0436             {"A", "B", "b", "c", "f",
0437 #if CINDEX_VERSION_MINOR >= 30
0438              "f",
0439 #endif
0440                          "main"},
0441             {"c", "A", "b", "B"}
0442     };
0443     QTest::newRow("function-arguments")
0444         << "class Abc; int f(Abc){\n "
0445         << CompletionItems{{1, 0}, {
0446             "Abc",
0447             "f",
0448         }};
0449     QTest::newRow("look-ahead int")
0450         << "struct LookAhead { int intItem;}; int main() {LookAhead* pInstance; LookAhead instance; int i =\n }"
0451         << CompletionItems{{1, 0}, {
0452             "LookAhead", "instance",
0453             "instance.intItem", "main",
0454             "pInstance", "pInstance->intItem",
0455         }};
0456     QTest::newRow("look-ahead class")
0457         << "class Class{}; struct LookAhead {Class classItem;}; int main() {LookAhead* pInstance; LookAhead instance; Class cl =\n }"
0458         << CompletionItems{{1, 0}, {
0459             "Class", "LookAhead",
0460             "instance", "instance.classItem",
0461             "main", "pInstance", "pInstance->classItem",
0462         }};
0463     QTest::newRow("look-ahead function argument")
0464         << "class Class{}; struct LookAhead {Class classItem;}; void function(Class cl);"
0465            "int main() {LookAhead* pInstance; LookAhead instance; function(\n }"
0466         << CompletionItems{{1, 0}, {
0467             "Class", "LookAhead", "function",
0468 #if CINDEX_VERSION_MINOR >= 30
0469             "function",
0470 #endif
0471             "instance", "instance.classItem",
0472             "main", "pInstance", "pInstance->classItem",
0473         }};
0474     QTest::newRow("look-ahead function primary type argument")
0475         << "struct LookAhead {double doubleItem;}; void function(double d);"
0476            "int main() {LookAhead* pInstance; LookAhead instance; function(\n }"
0477         << CompletionItems{{1, 0}, {
0478             "LookAhead",
0479             "function",
0480 #if CINDEX_VERSION_MINOR >= 30
0481             "function",
0482 #endif
0483             "instance",
0484             "instance.doubleItem", "main",
0485             "pInstance", "pInstance->doubleItem",
0486         }};
0487     QTest::newRow("look-ahead typedef")
0488         << "typedef double DOUBLE; struct LookAhead {DOUBLE doubleItem;};"
0489            "int main() {LookAhead* pInstance; LookAhead instance; double i =\n "
0490         << CompletionItems{{1, 0}, {
0491             "DOUBLE", "LookAhead",
0492             "instance", "instance.doubleItem",
0493             "main", "pInstance", "pInstance->doubleItem",
0494         }};
0495     QTest::newRow("look-ahead pointer")
0496         << "struct LookAhead {int* pInt;};"
0497            "int main() {LookAhead* pInstance; LookAhead instance; int* i =\n "
0498         << CompletionItems{{1, 0}, {
0499             "LookAhead", "instance",
0500             "instance.pInt", "main",
0501             "pInstance", "pInstance->pInt",
0502         }};
0503     QTest::newRow("look-ahead template")
0504         << "template <typename T> struct LookAhead {int intItem;};"
0505            "int main() {LookAhead<int>* pInstance; LookAhead<int> instance; int i =\n "
0506         << CompletionItems{{1, 0}, {
0507             "LookAhead", "instance",
0508             "instance.intItem", "main",
0509             "pInstance", "pInstance->intItem",
0510         }};
0511     QTest::newRow("look-ahead template parameter substitution")
0512         << "template <typename T> struct LookAhead {T itemT;};"
0513            "int main() {LookAhead<int>* pInstance; LookAhead<int> instance; int i =\n "
0514         << CompletionItems{{1, 0}, {
0515             "LookAhead", "instance",
0516             "instance.itemT", "main",
0517             "pInstance", "pInstance->itemT",
0518         }};
0519     QTest::newRow("look-ahead item access")
0520         << "class Class { public: int publicInt; protected: int protectedInt; private: int privateInt;};"
0521            "int main() {Class cl; int i =\n "
0522         << CompletionItems{{1, 0}, {
0523             "Class", "cl",
0524             "cl.publicInt",
0525             "main",
0526         }};
0527 
0528     QTest::newRow("look-ahead auto item")
0529         << "struct LookAhead { int intItem; };"
0530            "int main() {auto instance = LookAhead(); int i = \n "
0531         << CompletionItems{{1, 0}, {
0532             "LookAhead",
0533             "instance",
0534             "instance.intItem",
0535             "main"
0536         }};
0537 
0538     QTest::newRow("variadic template recursive class")
0539         << R"(
0540 template <typename Head, typename ...Tail>
0541 struct my_class : Head, my_class<Tail...>
0542 {
0543     using base = Head;
0544 };)"
0545         << CompletionItems{{3, 17}, { "Head", "Tail", "my_class" }};
0546 }
0547 
0548 
0549 void TestCodeCompletion::testClangCodeCompletionType()
0550 {
0551     QFETCH(QString, fileExtension);
0552     QFETCH(QString, code);
0553     QFETCH(CompletionItems, expectedItems);
0554     QFETCH(QString, expedtedItem);
0555     QFETCH(QString, expectedType);
0556 
0557     auto executeItem = [=] (const ClangCodeCompletionItemTester& tester) {
0558         auto item = tester.findItem(expedtedItem);
0559         QVERIFY(item);
0560         auto declaration = item->declaration();
0561         QVERIFY(declaration);
0562         QCOMPARE(declaration->abstractType()->toString(), expectedType);
0563     };
0564 
0565     executeCompletionTest(code, expectedItems, NoMacroOrBuiltin, executeItem, fileExtension);
0566 }
0567 
0568 void TestCodeCompletion::testClangCodeCompletionType_data()
0569 {
0570     QTest::addColumn<QString>("fileExtension");
0571     QTest::addColumn<QString>("code");
0572     QTest::addColumn<CompletionItems>("expectedItems");
0573     QTest::addColumn<QString>("expedtedItem");
0574     QTest::addColumn<QString>("expectedType");
0575 
0576     QTest::newRow("bug409041")
0577         << "c"
0578         << "typedef struct { int bitmask[1]; } bitmask_a;\n"
0579            "typedef struct { int bitmask[6]; } bitmask_c;\n"
0580            "typedef union { bitmask_c bitmask; } bitmask_union;\n"
0581            "int main() { bitmask_union u;\n"
0582            "u. \n "
0583         << CompletionItems{{4, 2}, { "bitmask" }}
0584         << "bitmask"
0585         << "bitmask_c";
0586 }
0587 
0588 void TestCodeCompletion::testReplaceMemberAccess()
0589 {
0590     QFETCH(QString, code);
0591     QFETCH(CompletionItems, expectedItems);
0592 
0593     executeMemberAccessReplacerTest(code, expectedItems);
0594 }
0595 
0596 void TestCodeCompletion::testReplaceMemberAccess_data()
0597 {
0598     QTest::addColumn<QString>("code");
0599     QTest::addColumn<CompletionItems>("expectedItems");
0600 
0601     {
0602         const auto code = QString::fromLatin1(
0603             "struct Struct { void function(); };"
0604             "int main() { Struct s; \ns");
0605 
0606         CompletionItems cis;
0607         cis.position = {1, 3};
0608         cis.completions = QStringList{"function"};
0609         cis.code = code + ".";
0610         QTest::newRow("replace arrow to dot") << (code + "->") << cis;
0611     }
0612 
0613     {
0614         const auto code = QString::fromLatin1(
0615             "struct Struct { void function(); };"
0616             "int main() { Struct* s; \ns");
0617 
0618         CompletionItems cis;
0619         cis.position = {1, 3};
0620         cis.completions = QStringList{"function"};
0621         cis.code = code + "->";
0622         QTest::newRow("replace dot to arrow") << (code + ".") << cis;
0623     }
0624 
0625     {
0626         const auto code = QString::fromLatin1("int main() { double a = \n0.  ");
0627 
0628         CompletionItems cis;
0629         cis.position = {1, 2};
0630         cis.completions = QStringList{};
0631         cis.code = code;
0632         QTest::newRow("no replacement needed") << code << cis;
0633     }
0634 
0635     // See https://bugs.kde.org/show_bug.cgi?id=460870
0636     {
0637         const auto code = QString::fromLatin1(
0638             "struct S { double a; double b; };"
0639             "int main() { S c[2] = { {.6, \n.");
0640 
0641         CompletionItems cis;
0642         cis.position = {1, 1};
0643         cis.completions = QStringList{};
0644         cis.code = code;
0645         QTest::newRow("no replacement in aggregate initializer with floating point number") << code << cis;
0646     }
0647 
0648     // See https://bugs.kde.org/show_bug.cgi?id=468605
0649     {
0650         const auto code = QString::fromLatin1(
0651             "struct Test { int a; float b; };"
0652             "int main(int argc, char **argv) { Test foo{1, \n2.");
0653 
0654         CompletionItems cis;
0655         cis.position = {1, 2};
0656         cis.completions = QStringList{};
0657         cis.code = code;
0658         QTest::newRow("no replacement in aggregate initializer with floating point number 2") << code << cis;
0659     }
0660 
0661     {
0662         const auto code = QString::fromLatin1(
0663             "typedef struct myStruct { int x; } myStruct;"
0664             "myStruct fn() { return (myStruct) { \n.");
0665 
0666         CompletionItems cis;
0667         cis.position = {1, 1};
0668         cis.completions = QStringList{"x"};
0669         cis.code = code;
0670         QTest::newRow("no replacement in aggregate initializer with member") << code << cis;
0671     }
0672 }
0673 
0674 void TestCodeCompletion::testVirtualOverride()
0675 {
0676     QFETCH(QString, code);
0677     QFETCH(CompletionItems, expectedItems);
0678 
0679     executeCompletionTest(code, expectedItems, ClangCodeCompletionContext::NoClangCompletion);
0680 }
0681 
0682 void TestCodeCompletion::testVirtualOverride_data()
0683 {
0684     QTest::addColumn<QString>("code");
0685     QTest::addColumn<CompletionItems>("expectedItems");
0686 
0687     QTest::newRow("basic")
0688         <<  "class Foo { virtual void foo(); virtual void foo(char c); virtual char foo(char c, int i, double d); };\n"
0689             "class Bar : Foo \n{void foo(char c) override;\n}"
0690         << CompletionItems{{3, 1}, {"foo()", "foo(char c, int i, double d)"}};
0691 
0692     QTest::newRow("template")
0693         << "template<class T1, class T2> class Foo { virtual T2 foo(T1 a, T2 b, int i); virtual T2 overridden(T1 a); } ;\n"
0694            "class Bar : Foo<char, double> \n{double overridden(char a) override;\n}"
0695         << CompletionItems{{3, 1}, {"foo(char a, double b, int i)"}};
0696 
0697     QTest::newRow("nested-template")
0698         << "template<class T1, class T2> class Foo { virtual T2 foo(T1 a, T2 b, int i); virtual T2 overridden(T1 a, T2 b, int i); } ;\n"
0699            "template<class T1, class T2> class Baz { };\n"
0700            "class Bar : Foo<char, Baz<char, double>> \n{Baz<char, double> overridden(char a, Baz<char, double> b, int i) override;\n}"
0701         << CompletionItems{{4, 1}, {"foo(char a, Baz<char, double> b, int i)"}};
0702 
0703     QTest::newRow("multi")
0704         << "class Foo { virtual int foo(int i); virtual int overridden(int i); };\n"
0705            "class Baz { virtual char baz(char c); };\n"
0706            "class Bar : Foo, Baz \n{int overridden(int i) override;\n}"
0707         << CompletionItems{{4, 1}, {"baz(char c)", "foo(int i)"}};
0708 
0709     QTest::newRow("deep")
0710         << "class Foo { virtual int foo(int i); virtual int overridden(int i); };\n"
0711            "class Baz : Foo { };\n"
0712            "class Bar : Baz \n{int overridden(int i) override;\n}"
0713         << CompletionItems{{4, 1}, {"foo(int i)"}};
0714 
0715     QTest::newRow("repeated")
0716         << "class Foo { virtual int foo(int i); virtual int overridden(int i); };\n"
0717            "class Baz : Foo { int foo(int i) override; };\n"
0718            "class Bar : Baz \n{int overridden(int i) override;\n}"
0719         << CompletionItems{{4, 1}, {"foo(int i)"}};
0720 
0721     QTest::newRow("pure")
0722         << "class Foo { virtual void foo() = 0; virtual void overridden() = 0;};\n"
0723            "class Bar : Foo \n{void overridden() override;\n};"
0724         << CompletionItems{{3, 0}, {"foo() = 0"}};
0725 
0726     QTest::newRow("repeated-pure")
0727         << "class Foo { virtual void foo() = 0; virtual void overridden() = 0; };\n"
0728            "class Baz : Foo { void foo() override; };\n"
0729            "class Bar : Baz \n{void overridden() override;\n}"
0730         << CompletionItems{{4, 1}, {"foo()"}};
0731 
0732     QTest::newRow("const")
0733         << "class Foo { virtual void foo(const int b) const; virtual void overridden(const int b) const; }\n;"
0734            "class Bar : Foo \n{void overridden(const int b) const override;\n}"
0735         << CompletionItems{{3, 1}, {"foo(const int b) const"}};
0736 
0737     QTest::newRow("dont-override")
0738         << R"(class A {
0739                   virtual void something() = 0;
0740               };
0741               class B : public A
0742               {
0743               public:
0744                   void foo();
0745               };
0746               void B::foo() {}
0747         )" << CompletionItems{{8, 14}, {}};
0748 }
0749 
0750 void TestCodeCompletion::testOverrideExecute()
0751 {
0752     QFETCH(QString, code);
0753     QFETCH(CompletionItems, expectedItems);
0754     QFETCH(QString, itemToExecute);
0755     QFETCH(QString, cppStandard);
0756     QFETCH(QString, expectedCode);
0757 
0758     QTemporaryDir directory;
0759     TestProject testProject {Path{directory.path()}};
0760     auto t = testProject.path().toLocalFile();
0761     auto configGroup = testProject.projectConfiguration()->group(definesAndIncludesConfigName).group("ProjectPath0");
0762     configGroup.writeEntry("Path", ".");
0763     configGroup.writeEntry("parserArguments", cppStandard);
0764     configGroup.sync();
0765     m_projectController->addProject(&testProject);
0766 
0767     TestFile file(code, QStringLiteral("cpp"), &testProject, directory.path());
0768     QVERIFY(file.parseAndWait(TopDUContext::AllDeclarationsContextsUsesAndAST));
0769 
0770     auto executeItem = [=] (const ClangCodeCompletionItemTester& tester) {
0771         auto item = tester.findItem(itemToExecute);
0772         QVERIFY(item);
0773         auto view = createView(tester.completionContext->duContext()->url().toUrl());
0774         DUChainReadLocker lock;
0775         item->execute(view.get(), view->document()->wordRangeAt(expectedItems.position));
0776         QCOMPARE(view->document()->text(), expectedCode);
0777     };
0778     executeCompletionTest(file.topContext(), expectedItems, NoMacroOrBuiltin, executeItem);
0779     m_projectController->closeProject(&testProject);
0780 }
0781 
0782 void TestCodeCompletion::testOverrideExecute_data()
0783 {
0784     QTest::addColumn<QString>("code");
0785     QTest::addColumn<CompletionItems>("expectedItems");
0786     QTest::addColumn<QString>("itemToExecute");
0787     QTest::addColumn<QString>("cppStandard");
0788     QTest::addColumn<QString>("expectedCode");
0789 
0790     QTest::newRow("override-modern")
0791         << "class Foo { virtual int bar(char c); };\nclass Baz : Foo\n{\n};"
0792         << CompletionItems{{3, 0}, {"Baz", "Foo", "bar(char c)"}}
0793         << "bar(char c)"
0794         << "-std=c++11"
0795         << "class Foo { virtual int bar(char c); };\n"
0796            "class Baz : Foo\n{\nint bar(char c) override;};";
0797 
0798     QTest::newRow("override-non-modern")
0799         << "class Foo { virtual int bar(char c); };\nclass Baz : Foo\n{\n};"
0800         << CompletionItems{{3, 0}, {"Baz", "Foo", "bar(char c)"}}
0801         << "bar(char c)"
0802         << "-std=c++98"
0803         << "class Foo { virtual int bar(char c); };\n"
0804            "class Baz : Foo\n{\nint bar(char c);};";
0805 
0806      QTest::newRow("override-unknown")
0807         << "class Foo { virtual int bar(char c); };\nclass Baz : Foo\n{\n};"
0808         << CompletionItems{{3, 0}, {"Baz", "Foo", "bar(char c)"}}
0809         << "bar(char c)"
0810         << "-std=c++17"
0811         << "class Foo { virtual int bar(char c); };\n"
0812            "class Baz : Foo\n{\nint bar(char c) override;};";
0813 }
0814 
0815 
0816 void TestCodeCompletion::testImplement()
0817 {
0818     QFETCH(QString, code);
0819     QFETCH(CompletionItems, expectedItems);
0820 
0821     executeCompletionTest(code, expectedItems, ClangCodeCompletionContext::NoClangCompletion);
0822 }
0823 
0824 void TestCodeCompletion::testImplement_data()
0825 {
0826     QTest::addColumn<QString>("code");
0827     QTest::addColumn<CompletionItems>("expectedItems");
0828 
0829     QTest::newRow("basic")
0830         << "int foo(char c, int i); \n"
0831         << CompletionItems{{1, 0}, {"foo(char c, int i)"}};
0832 
0833     QTest::newRow("class")
0834         << "class Foo { \n"
0835            "int bar(char c, int i); \n\n"
0836            "}; \n"
0837         << CompletionItems{{2, 0}, {}};
0838 
0839     QTest::newRow("class2")
0840         << "class Foo { \n"
0841             "int bar(char c, int i); \n\n"
0842             "}; \n"
0843         << CompletionItems{{4, 0}, {"Foo::bar(char c, int i)"}};
0844 
0845     QTest::newRow("namespace")
0846         << "namespace Foo { \n"
0847            "int bar(char c, int i); \n\n"
0848            "}; \n"
0849         << CompletionItems{{2, 0}, {"bar(char c, int i)"}};
0850 
0851     QTest::newRow("anonymous-namespace")
0852         << R"(
0853                 namespace {
0854                     int bar(char c, int i);
0855                 };
0856             )"
0857         << CompletionItems{{3, 0}, {"bar(char c, int i)"}};
0858 
0859     QTest::newRow("anonymous-namespace2")
0860         << R"(
0861                 namespace {
0862                     int bar(char c, int i);
0863                 };
0864            )"
0865         << CompletionItems{{4, 0}, {}};
0866 
0867     QTest::newRow("namespace2")
0868         << "namespace Foo { \n"
0869            "int bar(char c, int i); \n\n"
0870            "}; \n"
0871         << CompletionItems{{4, 0}, {"Foo::bar(char c, int i)"}};
0872 
0873     QTest::newRow("two-namespace")
0874         << "namespace Foo { int bar(char c, int i); };\n"
0875            "namespace Foo {\n"
0876            "};\n"
0877         << CompletionItems{{2, 0}, {"bar(char c, int i)"}};
0878 
0879     QTest::newRow("destructor")
0880         << "class Foo { ~Foo(); }\n"
0881         << CompletionItems{{1, 0}, {"Foo::~Foo()"}};
0882 
0883     QTest::newRow("constructor")
0884         << "class Foo { \n"
0885                  "Foo(int i); \n"
0886                  "}; \n"
0887         << CompletionItems{{3, 1}, {"Foo::Foo(int i)"}};
0888 
0889     QTest::newRow("template")
0890         << "template<typename T> class Foo { T bar(T t); };\n"
0891         << CompletionItems{{1, 1}, {"Foo<T>::bar(T t)"}};
0892 
0893     QTest::newRow("specialized-template")
0894         << "template<typename T> class Foo { \n"
0895            "T bar(T t); \n"
0896            "}; \n"
0897            "template<typename T> T Foo<T>::bar(T t){} \n"
0898            "template<> class Foo<int> { \n"
0899            "int bar(int t); \n"
0900            "}\n"
0901         << CompletionItems{{6, 1}, {"Foo<int>::bar(int t)"}};
0902 
0903     QTest::newRow("nested-class")
0904         << "class Foo { \n"
0905            "class Bar { \n"
0906            "int baz(char c, int i); \n\n"
0907            "}; \n\n"
0908            "}; \n\n"
0909         << CompletionItems {{3, 1}, {}};
0910 
0911     QTest::newRow("nested-class2")
0912         << "class Foo { \n"
0913            "class Bar { \n"
0914            "int baz(char c, int i); \n\n"
0915            "}; \n\n"
0916            "}; \n\n"
0917         << CompletionItems {{5, 1}, {}};
0918 
0919     QTest::newRow("nested-class3")
0920         << "class Foo { \n"
0921            "class Bar { \n"
0922            "int baz(char c, int i); \n\n"
0923            "}; \n\n"
0924            "}; \n\n"
0925         << CompletionItems {{7, 1}, {"Foo::Bar::baz(char c, int i)"}};
0926 
0927     QTest::newRow("nested-namespace")
0928         << "namespace Foo { \n"
0929            "namespace Bar { \n"
0930            "int baz(char c, int i); \n\n"
0931            "}; \n\n"
0932            "}; \n\n"
0933         << CompletionItems {{3, 1}, {"baz(char c, int i)"}};
0934 
0935     QTest::newRow("nested-namespace2")
0936         << "namespace Foo { \n"
0937            "namespace Bar { \n"
0938            "int baz(char c, int i); \n\n"
0939            "}; \n\n"
0940            "}; \n\n"
0941         << CompletionItems {{5, 1}, {"Bar::baz(char c, int i)"}};
0942 
0943     QTest::newRow("nested-namespace3")
0944         << "namespace Foo { \n"
0945            "namespace Bar { \n"
0946            "int baz(char c, int i); \n\n"
0947            "}; \n\n"
0948            "}; \n\n"
0949         << CompletionItems {{7, 1}, {"Foo::Bar::baz(char c, int i)"}};
0950 
0951     QTest::newRow("partial-template")
0952         << "template<typename T> class Foo { \n"
0953            "template<typename U> class Bar;\n"
0954            "template<typename U> class Bar<U*> { void baz(T t, U u); }\n"
0955            "}\n"
0956         << CompletionItems{{5,1}, {"Foo<T>::Bar<U *>::baz(T t, U u)"}};
0957 
0958     QTest::newRow("variadic")
0959         << "int foo(...); int bar(int i, ...); \n"
0960         << CompletionItems{{1, 1}, {"bar(int i, ...)", "foo(...)"}};
0961 
0962     QTest::newRow("const")
0963         << "class Foo { int bar() const; };"
0964         << CompletionItems{{3, 1}, {"Foo::bar() const"}};
0965 
0966     QTest::newRow("noexcept")
0967         << "class Foo { int bar() noexcept; };"
0968         << CompletionItems{{3, 1}, {"Foo::bar() noexcept"}};
0969 
0970     QTest::newRow("throw()")
0971         << "class Foo { int bar() throw(); };"
0972         << CompletionItems{{3, 1}, {"Foo::bar() throw()"}};
0973 
0974     QTest::newRow("multiple-methods")
0975         << "class Foo { int bar(); void foo(); char asdf() const; };"
0976         << CompletionItems{{1, 1}, {"Foo::asdf() const", "Foo::bar()", "Foo::foo()"}};
0977 
0978     // explicitly deleted/defaulted functions should not appear in the implements completion
0979     QTest::newRow("deleted-copy-ctor")
0980         << "struct S { S(); S(const S&) = /*some comment*/ delete; };"
0981         << CompletionItems{{1,1}, {"S::S()"}};
0982     QTest::newRow("deleted-overload-member")
0983         << "struct Foo {\n"
0984            "  int x();\n"
0985            "  int x(int) =delete;\n"
0986            "};\n"
0987         << CompletionItems{{5,1}, {"Foo::x()"}};
0988     QTest::newRow("deleted-overload-global")
0989         << "int x();\n"
0990            "int x(int)=  delete;\n"
0991         << CompletionItems{{2,1}, {"x()"}};
0992     QTest::newRow("defaulted-copy-ctor")
0993         << "struct S { S(); S(const S&) = default; };"
0994         << CompletionItems{{1,1}, {"S::S()"}};
0995     QTest::newRow("defaulted-dtor")
0996         << "struct Foo {\n"
0997            "  Foo();\n"
0998            "  ~Foo() =default;\n"
0999            "};\n"
1000         << CompletionItems{{5,1}, {"Foo::Foo()"}};
1001 
1002     QTest::newRow("bug355163")
1003         << R"(
1004                 #include <type_traits>
1005                 namespace test {
1006 
1007                 template<typename T, typename U>
1008                 struct IsSafeConversion : public std::is_same<T, typename std::common_type<T, U>::type>
1009                 {
1010 
1011                 };
1012 
1013                 } // namespace test
1014             )"
1015         << CompletionItems{{7,0}, {}};
1016 
1017     QTest::newRow("bug355954")
1018         << R"(
1019                 struct Hello {
1020                     struct Private;
1021                 };
1022 
1023                 struct Hello::Private {
1024                     void test();
1025                 };
1026             )"
1027         << CompletionItems{{8,0}, {"Hello::Private::test()"}};
1028 
1029     QTest::newRow("lineOfNextFunction")
1030         << "void foo();\nvoid bar() {}"
1031         << CompletionItems{{1,0}, {"foo()"}};
1032 
1033     QTest::newRow("pure")
1034         << R"(
1035                 struct Hello {
1036                     virtual void foo() = 0;
1037                     virtual void bar();
1038                 };
1039             )"
1040         << CompletionItems{{5, 0}, {"Hello::bar()"}};
1041 
1042     QTest::newRow("bug368544")
1043         << R"(
1044                 class Klass {
1045                 public:
1046                     template <typename T>
1047                     void func(int a, T x, int b) const;
1048                 };
1049             )"
1050         << CompletionItems{{6, 0}, {"Klass::func(int a, T x, int b) const"}};
1051 
1052 }
1053 
1054 void TestCodeCompletion::testImplementOtherFile()
1055 {
1056     TestFile header1(QStringLiteral("void foo();"), QStringLiteral("h"));
1057     QVERIFY(header1.parseAndWait());
1058     TestFile header2(QStringLiteral("void bar();"), QStringLiteral("h"));
1059     QVERIFY(header2.parseAndWait());
1060     TestFile impl(QString("#include \"%1\"\n"
1061                           "#include \"%2\"\n"
1062                           "void asdf();\n\n")
1063                     .arg(header1.url().str(), header2.url().str()),
1064                   QStringLiteral("cpp"), &header1);
1065 
1066     CompletionItems expectedItems{{3,1}, {"asdf()", "foo()"}};
1067     QVERIFY(impl.parseAndWait(TopDUContext::AllDeclarationsContextsUsesAndAST));
1068     executeCompletionTest(impl.topContext(), expectedItems);
1069 }
1070 
1071 void TestCodeCompletion::testImplementAfterEdit()
1072 {
1073     TestFile header1(QStringLiteral("void foo();"), QStringLiteral("h"));
1074     QVERIFY(header1.parseAndWait());
1075     TestFile impl(QString("#include \"%1\"\n"
1076                           "void asdf() {}\nvoid bar() {}")
1077                     .arg(header1.url().str()),
1078                   QStringLiteral("cpp"), &header1);
1079 
1080     auto document = ICore::self()->documentController()->openDocument(impl.url().toUrl());
1081 
1082     QVERIFY(impl.parseAndWait(TopDUContext::AllDeclarationsContextsUsesAndAST));
1083 
1084     CompletionItems expectedItems{{2,0}, {"foo()"}};
1085     executeCompletionTest(impl.topContext(), expectedItems);
1086 
1087     document->textDocument()->insertText(expectedItems.position, QStringLiteral("\n"));
1088     expectedItems.position.setLine(3);
1089 
1090     executeCompletionTest(impl.topContext(), expectedItems);
1091 
1092     document->close(IDocument::Discard);
1093 }
1094 
1095 void TestCodeCompletion::testInvalidCompletions()
1096 {
1097     QFETCH(QString, code);
1098     QFETCH(CompletionItems, expectedItems);
1099 
1100     executeCompletionTest(code, expectedItems);
1101 }
1102 
1103 void TestCodeCompletion::testInvalidCompletions_data()
1104 {
1105     QTest::addColumn<QString>("code");
1106     QTest::addColumn<CompletionItems>("expectedItems");
1107 
1108     QTest::newRow("invalid-context-incomment")
1109         << "class Foo { int bar() const; };\n/*\n*/"
1110         << CompletionItems{{2, 0}, {}};
1111 }
1112 
1113 void TestCodeCompletion::testIncludePathCompletion_data()
1114 {
1115     QTest::addColumn<QString>("code");
1116     QTest::addColumn<KTextEditor::Cursor>("cursor");
1117     QTest::addColumn<QString>("itemId");
1118     QTest::addColumn<QString>("result");
1119 
1120     QTest::newRow("global-1") << QStringLiteral("#include ") << KTextEditor::Cursor(0, 9)
1121                               << QStringLiteral("iostream") << QStringLiteral("#include <iostream>");
1122     QTest::newRow("global-2") << QStringLiteral("#include <") << KTextEditor::Cursor(0, 9)
1123                               << QStringLiteral("iostream") << QStringLiteral("#include <iostream>");
1124     QTest::newRow("global-3") << QStringLiteral("#include <") << KTextEditor::Cursor(0, 10)
1125                               << QStringLiteral("iostream") << QStringLiteral("#include <iostream>");
1126     QTest::newRow("global-4") << QStringLiteral("#  include <") << KTextEditor::Cursor(0, 12)
1127                               << QStringLiteral("iostream") << QStringLiteral("#  include <iostream>");
1128     QTest::newRow("global-5") << QStringLiteral("#  include   <") << KTextEditor::Cursor(0, 14)
1129                               << QStringLiteral("iostream") << QStringLiteral("#  include   <iostream>");
1130     QTest::newRow("global-6") << QStringLiteral("#  include   <> /* 1 */") << KTextEditor::Cursor(0, 14)
1131                               << QStringLiteral("iostream") << QStringLiteral("#  include   <iostream> /* 1 */");
1132     QTest::newRow("global-7") << QStringLiteral("#  include /* 1 */ <> /* 1 */") << KTextEditor::Cursor(0, 21)
1133                               << QStringLiteral("iostream") << QStringLiteral("#  include /* 1 */ <iostream> /* 1 */");
1134     QTest::newRow("global-8") << QStringLiteral("# /* 1 */ include /* 1 */ <> /* 1 */") << KTextEditor::Cursor(0, 28)
1135                               << QStringLiteral("iostream") << QStringLiteral("# /* 1 */ include /* 1 */ <iostream> /* 1 */");
1136     QTest::newRow("global-9") << QStringLiteral("#include <cstdint>") << KTextEditor::Cursor(0, 10)
1137                               << QStringLiteral("iostream") << QStringLiteral("#include <iostream>");
1138     QTest::newRow("global-10") << QStringLiteral("#include <cstdint>") << KTextEditor::Cursor(0, 14)
1139                               << QStringLiteral("cstdint") << QStringLiteral("#include <cstdint>");
1140     QTest::newRow("global-11") << QStringLiteral("#include <cstdint>") << KTextEditor::Cursor(0, 17)
1141                               << QStringLiteral("cstdint") << QStringLiteral("#include <cstdint>");
1142     QTest::newRow("local-0") << QStringLiteral("#include \"") << KTextEditor::Cursor(0, 10)
1143                               << QStringLiteral("foo/") << QStringLiteral("#include \"foo/\"");
1144     QTest::newRow("local-1") << QStringLiteral("#include \"foo/\"") << KTextEditor::Cursor(0, 14)
1145                               << QStringLiteral("bar/") << QStringLiteral("#include \"foo/bar/\"");
1146     QTest::newRow("local-2") << QStringLiteral("#include \"foo/") << KTextEditor::Cursor(0, 14)
1147                               << QStringLiteral("bar/") << QStringLiteral("#include \"foo/bar/\"");
1148     QTest::newRow("local-3") << QStringLiteral("# /* 1 */ include /* 1 */ \"\" /* 1 */") << KTextEditor::Cursor(0, 28)
1149                               << QStringLiteral("foo/") << QStringLiteral("# /* 1 */ include /* 1 */ \"foo/\" /* 1 */");
1150     QTest::newRow("local-4") << QStringLiteral("# /* 1 */ include /* 1 */ \"foo/\" /* 1 */") << KTextEditor::Cursor(0, 31)
1151                               << QStringLiteral("bar/") << QStringLiteral("# /* 1 */ include /* 1 */ \"foo/bar/\" /* 1 */");
1152     QTest::newRow("local-5") << QStringLiteral("#include \"foo/\"") << KTextEditor::Cursor(0, 10)
1153                               << QStringLiteral("foo/") << QStringLiteral("#include \"foo/\"");
1154     QTest::newRow("local-6") << QStringLiteral("#include \"foo/asdf\"") << KTextEditor::Cursor(0, 10)
1155                               << QStringLiteral("foo/") << QStringLiteral("#include \"foo/\"");
1156     QTest::newRow("local-7") << QStringLiteral("#include \"foo/asdf\"") << KTextEditor::Cursor(0, 14)
1157                               << QStringLiteral("bar/") << QStringLiteral("#include \"foo/bar/\"");
1158     QTest::newRow("dash-1") << QStringLiteral("#include \"") << KTextEditor::Cursor(0, 10)
1159                               << QStringLiteral("dash-file.h") << QStringLiteral("#include \"dash-file.h\"");
1160     QTest::newRow("dash-2") << QStringLiteral("#include \"dash-") << KTextEditor::Cursor(0, 15)
1161                               << QStringLiteral("dash-file.h") << QStringLiteral("#include \"dash-file.h\"");
1162     QTest::newRow("dash-4") << QStringLiteral("#include \"dash-file.h\"") << KTextEditor::Cursor(0, 13)
1163                               << QStringLiteral("dash-file.h") << QStringLiteral("#include \"dash-file.h\"");
1164     QTest::newRow("dash-5") << QStringLiteral("#include \"dash-file.h\"") << KTextEditor::Cursor(0, 14)
1165                               << QStringLiteral("dash-file.h") << QStringLiteral("#include \"dash-file.h\"");
1166     QTest::newRow("dash-6") << QStringLiteral("#include \"dash-file.h\"") << KTextEditor::Cursor(0, 15)
1167                               << QStringLiteral("dash-file.h") << QStringLiteral("#include \"dash-file.h\"");
1168 }
1169 
1170 void TestCodeCompletion::testIncludePathCompletion()
1171 {
1172     QFETCH(QString, code);
1173     QFETCH(KTextEditor::Cursor, cursor);
1174     QFETCH(QString, itemId);
1175     QFETCH(QString, result);
1176 
1177     QTemporaryDir tempDir;
1178     QDir dir(tempDir.path());
1179     QVERIFY(dir.mkpath("foo/bar/asdf"));
1180     TestFile file(code, QStringLiteral("cpp"), nullptr, tempDir.path());
1181     {
1182         QFile otherFile(tempDir.path() + "/dash-file.h");
1183         QVERIFY(otherFile.open(QIODevice::WriteOnly));
1184     }
1185     IncludeTester tester(executeIncludePathCompletion(&file, cursor));
1186     QVERIFY(tester.completionContext);
1187     QVERIFY(tester.completionContext->isValid());
1188 
1189     auto item = tester.findItem(itemId);
1190     QVERIFY(item);
1191 
1192     auto view = createView(file.url().toUrl());
1193     QVERIFY(view.get());
1194     auto doc = view->document();
1195     item->execute(view.get(), KTextEditor::Range(cursor, cursor));
1196     QCOMPARE(doc->text(), result);
1197 
1198     const auto newCursor = view->cursorPosition();
1199     QCOMPARE(newCursor.line(), cursor.line());
1200     if (!itemId.endsWith('/')) {
1201         // file inserted, cursor should be at end of line
1202         QCOMPARE(newCursor.column(), doc->lineLength(cursor.line()));
1203     } else {
1204         // directory inserted, cursor should be before the " or >
1205         const auto cursorChar = doc->characterAt(newCursor);
1206         QVERIFY(cursorChar == '"' || cursorChar == '>');
1207     }
1208 }
1209 
1210 void TestCodeCompletion::testIncludePathCompletionLocal()
1211 {
1212     TestFile header(QStringLiteral("int foo() { return 42; }\n"), QStringLiteral("h"));
1213     TestFile impl(QStringLiteral("#include \""), QStringLiteral("cpp"), &header);
1214 
1215     IncludeTester tester(executeIncludePathCompletion(&impl, {0, 10}));
1216     QVERIFY(tester.names.contains(header.url().toUrl().fileName()));
1217     QVERIFY(tester.names.contains("iostream"));
1218 }
1219 
1220 void TestCodeCompletion::testOverloadedFunctions()
1221 {
1222     TestFile file(QStringLiteral("void f(); int f(int); void f(int, double){\n "), QStringLiteral("cpp"));
1223     QVERIFY(file.parseAndWait(TopDUContext::AllDeclarationsContextsUsesAndAST));
1224     DUChainReadLocker lock;
1225     auto top = file.topContext();
1226     QVERIFY(top);
1227     const ParseSessionData::Ptr sessionData(dynamic_cast<ParseSessionData*>(top->ast().data()));
1228     QVERIFY(sessionData);
1229 
1230     lock.unlock();
1231 
1232     const auto context = createContext(top, sessionData, {1, 0});
1233     context->setFilters(NoMacroOrBuiltin);
1234     lock.lock();
1235     const auto tester = ClangCodeCompletionItemTester(context);
1236     QCOMPARE(tester.items.size(), 3);
1237     for (const auto& item : tester.items) {
1238         auto function = item->declaration()->type<FunctionType>();
1239         const QString display = item->declaration()->identifier().toString() + function->partToString(FunctionType::SignatureArguments);
1240         const QString itemDisplay = tester.itemData(item).toString() + tester.itemData(item, KTextEditor:: CodeCompletionModel::Arguments).toString();
1241         QCOMPARE(display, itemDisplay);
1242     }
1243 
1244     QVERIFY(tester.items[0]->declaration().data() != tester.items[1]->declaration().data());
1245     QVERIFY(tester.items[0]->declaration().data() != tester.items[2]->declaration().data());
1246     QVERIFY(tester.items[1]->declaration().data() != tester.items[2]->declaration().data());
1247 }
1248 
1249 void TestCodeCompletion::testCompletionPriority()
1250 {
1251     QFETCH(QString, code);
1252     QFETCH(CompletionPriorityItems, expectedItems);
1253 
1254     executeCompletionPriorityTest(code, expectedItems);
1255 }
1256 
1257 void TestCodeCompletion::testCompletionPriority_data()
1258 {
1259     QTest::addColumn<QString>("code");
1260     QTest::addColumn<CompletionPriorityItems>("expectedItems");
1261 
1262     QTest::newRow("pointer")
1263         << "class A{}; class B{}; class C : public B{}; int main(){A* a; B* b; C* c; b =\n "
1264         << CompletionPriorityItems{{1,0}, {{"a", 0, 21}, {"b", 6, 0},
1265         {"c", 2, 0, QStringLiteral("Pointer to derived class is not added to the Best Matches group")}}};
1266 
1267     QTest::newRow("look-ahead pointer") << "class A{}; class B{}; struct LookAhead {A* a; B* b;}; "
1268                                            "int main() {LookAhead* pInstance; LookAhead instance; A* a; B* b; b =\n "
1269                                         << CompletionPriorityItems{{1, 0},
1270                                                                    {{"a", 0, 21},
1271                                                                     {"b", 6, 0},
1272                                                                     {"pInstance", 0, 21},
1273                                                                     {"instance", 0, 34},
1274                                                                     {"pInstance->b", 6, 0},
1275                                                                     {"instance.b", 6, 0}}};
1276 
1277     QTest::newRow("class")
1278         << "class A{}; class B{}; class C : public B{}; int main(){A a; B b; C c; b =\n "
1279         << CompletionPriorityItems{{1,0}, {{"a", 0, 21}, {"b", 6, 0},
1280         {"c", 2, 0, QStringLiteral("Derived class is not added to the Best Matches group")}}};
1281 
1282     QTest::newRow("look-ahead class") << "class A{}; class B{}; struct LookAhead {A a; B b;}; "
1283                                          "int main() {LookAhead* pInstance; LookAhead instance; A a; B b; b =\n "
1284                                       << CompletionPriorityItems{{1, 0},
1285                                                                  {{"a", 0, 21},
1286                                                                   {"B", 4, 0},
1287                                                                   {"b", 6, 0},
1288                                                                   {"pInstance", 0, 34},
1289                                                                   {"instance", 0, 21},
1290                                                                   {"pInstance->b", 6, 0},
1291                                                                   {"instance.b", 6, 0}}};
1292 
1293     QTest::newRow("look-ahead class, no local variable of the type")
1294         << "class A{}; class B{}; struct LookAhead {A a; B b;}; "
1295            "int main() {LookAhead* pInstance; LookAhead instance; A a; B b =\n "
1296         << CompletionPriorityItems{{1, 0},
1297                                    {{"a", 0, 21},
1298                                     {"B", 4, 0},
1299                                     {"pInstance", 0, 34},
1300                                     {"instance", 0, 21},
1301                                     {"pInstance->b", 6, 0,
1302                                      QStringLiteral("No local variable with high match quality => look-ahead "
1303                                                     "match quality equals that of \"class B\" declaration")}}};
1304 
1305     QTest::newRow("primary-types") << "class A{}; int main(){A a; int b; bool c; bool d = \n "
1306                                    << CompletionPriorityItems{{1, 0}, {{"a", 0, 34}, {"b", 2, 0}, {"c", 6, 0}}};
1307 
1308     QTest::newRow("look-ahead primary-types")
1309         << "class A{}; struct LookAhead {A t; int x; bool y; unsigned z;}; "
1310            "int main() {LookAhead* pInstance; LookAhead instance; A a; int b; bool c; bool d = \n "
1311         << CompletionPriorityItems{{1, 0},
1312                                    {{"a", 0, 34},
1313                                     {"b", 2, 0},
1314                                     {"c", 6, 0},
1315                                     {"pInstance", 0, 34},
1316                                     {"instance", 0, 34},
1317                                     {"pInstance->x", 2, 0},
1318                                     {"instance.x", 2, 0},
1319                                     {"pInstance->y", 6, 0},
1320                                     {"instance.y", 6, 0}
1321 #if 0
1322         // This fails, not just xfails, because "instance.z" is not among completion-items.
1323         , {"instance.z", 2, 0, QStringLiteral("No local variable with the type")}
1324 #endif
1325                                    }};
1326 
1327     QTest::newRow("reference")
1328         << "class A{}; class B{}; class C : public B{};"
1329            "int main(){A tmp; A& a = tmp; C tmp2; C& c = tmp2; B& b =\n ;}"
1330         << CompletionPriorityItems{{1,0}, {{"a", 0, 21},
1331         {"c", 2, 0, QStringLiteral("Reference to derived class is not added to the Best Matches group")}}};
1332 
1333     QTest::newRow("typedef")
1334         << "struct A{}; struct B{}; typedef A AA; typedef B BB; void f(A p);"
1335            "int main(){ BB b; AA a; f(\n }"
1336         << CompletionPriorityItems{{1,0}, {{"a", 6, 0}, {"b", 0, 21}}};
1337 
1338     QTest::newRow("returnType")
1339         << "struct A{}; struct B{}; struct Test{A f();B g(); Test() { A a =\n }};"
1340         << CompletionPriorityItems{{1,0}, {{"f", 6, 0}, {"g", 0, 21}}};
1341 
1342     QTest::newRow("look-ahead returnType")
1343         << "struct A{}; struct B{}; struct LookAhead {A a; B b;}; "
1344            "struct Test{A f();B g(); Test() { LookAhead* pInstance; LookAhead instance; A a =\n }};"
1345         << CompletionPriorityItems{{1, 0},
1346                                    {{"f", 6, 0},
1347                                     {"g", 0, 21},
1348                                     {"pInstance", 0, 34},
1349                                     {"instance", 0, 21},
1350                                     {"pInstance->a", 6, 0},
1351                                     {"instance.a", 6, 0}}};
1352 
1353     QTest::newRow("template")
1354         << "template <typename T> class Class{}; template <typename T> class Class2{};"
1355            "int main(){ Class<int> a; Class2<int> b =\n }"
1356         << CompletionPriorityItems{{1,0}, {{"a", 0, 21}}};
1357 
1358     QTest::newRow("protected-access")
1359         << "class Base { protected: int m_protected; };"
1360            "class Derived: public Base {public: void g(){\n }};"
1361         << CompletionPriorityItems{{1,0}, {{"m_protected", 0, 37}}};
1362 
1363     QTest::newRow("protected-access2")
1364         << "class Base { protected: int m_protected; };"
1365            "class Derived: public Base {public: void f();};"
1366            "void Derived::f(){\n }"
1367         << CompletionPriorityItems{{1,0}, {{"m_protected", 0, 37}}};
1368 }
1369 
1370 void TestCodeCompletion::testVariableScope()
1371 {
1372     TestFile file(QStringLiteral("int var; \nvoid test(int var) {int tmp =\n }"), QStringLiteral("cpp"));
1373     QVERIFY(file.parseAndWait(TopDUContext::AllDeclarationsContextsUsesAndAST));
1374     DUChainReadLocker lock;
1375     auto top = file.topContext();
1376     QVERIFY(top);
1377     const ParseSessionData::Ptr sessionData(dynamic_cast<ParseSessionData*>(top->ast().data()));
1378     QVERIFY(sessionData);
1379 
1380     lock.unlock();
1381 
1382     const auto context = createContext(top, sessionData, {2, 0});
1383     context->setFilters(NoMacroOrBuiltin);
1384     lock.lock();
1385     const auto tester = ClangCodeCompletionItemTester(context);
1386 
1387     QCOMPARE(tester.items.size(), 3);
1388     auto item = tester.findItem(QStringLiteral("var"));
1389     VERIFY(item);
1390     QCOMPARE(item->declaration()->range().start, CursorInRevision(1, 14));
1391 }
1392 
1393 struct HintItem
1394 {
1395     QString hint;
1396     bool hasDeclaration;
1397     bool operator==(const HintItem& rhs) const
1398     {
1399         return std::tie(hint, hasDeclaration) == std::tie(rhs.hint, rhs.hasDeclaration);
1400     }
1401     bool operator<(const HintItem& rhs) const
1402     {
1403         return std::tie(hint, hasDeclaration) < std::tie(rhs.hint, rhs.hasDeclaration);
1404     }
1405     QByteArray toString() const
1406     {
1407         return "HintItem(" + hint.toUtf8() + ", " + (hasDeclaration ? "true" : "false") + ')';
1408     }
1409 };
1410 Q_DECLARE_METATYPE(HintItem)
1411 using HintItemList = QVector<HintItem>;
1412 namespace QTest {
1413 template<>
1414 char *toString(const HintItem& hint)
1415 {
1416     return qstrdup(hint.toString());
1417 }
1418 template<>
1419 char *toString(const HintItemList& hints)
1420 {
1421     QByteArray ba = "HintItemList(";
1422     for (int i = 0, c = hints.size(); i < c; ++i) {
1423         ba += hints[i].toString();
1424         if (i == c - 1) {
1425             ba += ')';
1426         } else {
1427             ba += ", ";
1428         }
1429     }
1430     return qstrdup(ba.constData());
1431 }
1432 }
1433 
1434 void TestCodeCompletion::testArgumentHintCompletion()
1435 {
1436     QFETCH(QString, code);
1437     QFETCH(CompletionItems, expectedItems);
1438     QFETCH(HintItemList, hints);
1439 
1440     executeCompletionTest(code, expectedItems, NoMacroOrBuiltin, [&](const ClangCodeCompletionItemTester& tester) {
1441         DUChainReadLocker lock;
1442         HintItemList actualHints;
1443         for (const auto& item : tester.items) {
1444             if (item->argumentHintDepth() == 1) {
1445                 actualHints << HintItem{
1446                     tester.itemData(item).toString() + tester.itemData(item, KTextEditor:: CodeCompletionModel::Arguments).toString(),
1447                     item->declaration()
1448                 };
1449             }
1450         }
1451         std::sort(hints.begin(), hints.end());
1452         std::sort(actualHints.begin(), actualHints.end());
1453         QEXPECT_FAIL("member function", "clang_getCompletionParent returns nothing, thus decl lookup fails", Continue);
1454         QEXPECT_FAIL("namespaced function", "clang_getCompletionParent returns nothing, thus decl lookup fails", Continue);
1455         QEXPECT_FAIL("namespaced constructor", "clang_getCompletionParent returns nothing, thus decl lookup fails", Continue);
1456         QCOMPARE(actualHints, hints);
1457     });
1458 }
1459 
1460 void TestCodeCompletion::testArgumentHintCompletion_data()
1461 {
1462 #if CINDEX_VERSION_MINOR < 30
1463     QSKIP("You need at least LibClang 3.7");
1464 #endif
1465     qRegisterMetaType<HintItemList>("HintItemList");
1466 
1467     QTest::addColumn<QString>("code");
1468     QTest::addColumn<CompletionItems>("expectedItems");
1469     QTest::addColumn<HintItemList>("hints");
1470 
1471     QTest::newRow("global function")
1472         << "void foo(int);\n"
1473            "int main() { \nfoo( "
1474         << CompletionItems{{2,4}, {
1475             "foo", "foo",
1476             "main"
1477         }}
1478         << HintItemList{{"foo(int)", true}};
1479 
1480     QTest::newRow("namespaced function")
1481         << "namespace ns { void foo(int); }\n"
1482            "int main() { \nns::foo( "
1483         << CompletionItems{{2,4}, {
1484             "foo"
1485         }}
1486         << HintItemList{{"foo(int)", true}};
1487 
1488     QTest::newRow("member function")
1489         << "struct Struct{ void foo(int);}\n"
1490            "int main() {Struct s; \ns.foo( "
1491         << CompletionItems{{2,6}, {
1492             "Struct", "foo",
1493             "main", "s"
1494         }}
1495         << HintItemList{{"foo(int)", true}};
1496 
1497     QTest::newRow("template function")
1498         << "template <typename T> void foo(T);\n"
1499            "int main() { \nfoo( "
1500         << CompletionItems{{2,6}, {
1501             "foo", "foo",
1502             "main"
1503         }}
1504         << HintItemList{{"foo(T)", true}};
1505 
1506     QTest::newRow("overloaded functions")
1507         << "void foo(int); void foo(int, double)\n"
1508            "int main() { \nfoo( "
1509         << CompletionItems{{2,6}, {
1510             "foo", "foo", "foo", "foo",
1511             "main"
1512         }}
1513         << HintItemList{
1514             {"foo(int)", true},
1515             {"foo(int, double)", true}
1516         };
1517 
1518     QTest::newRow("overloaded functions2")
1519         << "void foo(int); void foo(int, double)\n"
1520            "int main() { foo(1,\n  "
1521         << CompletionItems{{2,1}, {
1522             "foo", "foo", "foo",
1523             "main"
1524         }}
1525         << HintItemList{{"foo(int, double)", true}};
1526 
1527     QTest::newRow("constructor")
1528         << "struct foo { foo(int); foo(int, double); }\n"
1529            "int main() { foo f(\n "
1530         << CompletionItems{{2,1}, {
1531             "f", "foo", "foo", "foo", "foo", "foo",
1532             "main"
1533         }}
1534         << HintItemList{
1535             {"foo(int)", true},
1536             {"foo(int, double)", true},
1537             {"foo(foo &&)", false},
1538             {"foo(const foo &)", false}
1539         };
1540 
1541     QTest::newRow("constructor2")
1542         << "struct foo { foo(int); foo(int, double); }\n"
1543            "int main() { foo f(1,\n "
1544         << CompletionItems{{2,1}, {
1545             "f", "foo", "foo",
1546             "main"
1547         }}
1548         << HintItemList{
1549             {"foo(int, double)", true}
1550         };
1551 
1552     QTest::newRow("namespaced constructor")
1553         << "namespace ns { struct foo { foo(int); foo(int, double); } }\n"
1554            "int main() { ns::foo f(\n "
1555         << CompletionItems{{2,1}, {
1556             "f", "foo", "foo", "foo", "foo",
1557             "main", "ns"
1558         }}
1559         << HintItemList{
1560             {"foo(int)", true},
1561             {"foo(int, double)", true},
1562             {"foo(ns::foo &&)", false},
1563             {"foo(const ns::foo &)", false}
1564         };
1565 }
1566 
1567 void TestCodeCompletion::testArgumentHintCompletionDefaultParameters()
1568 {
1569 #if CINDEX_VERSION_MINOR < 30
1570     QSKIP("You need at least LibClang 3.7");
1571 #endif
1572 
1573     TestFile file(QStringLiteral("void f(int i, int j = 0, double k =1){\nf( "), QStringLiteral("cpp"));
1574     QVERIFY(file.parseAndWait(TopDUContext::AllDeclarationsContextsUsesAndAST));
1575     DUChainReadLocker lock;
1576     auto top = file.topContext();
1577     QVERIFY(top);
1578     const ParseSessionData::Ptr sessionData(dynamic_cast<ParseSessionData*>(top->ast().data()));
1579     QVERIFY(sessionData);
1580 
1581     lock.unlock();
1582 
1583     const auto context = createContext(top, sessionData, {1, 2});
1584     context->setFilters(NoMacroOrBuiltin);
1585     lock.lock();
1586     const auto tester = ClangCodeCompletionItemTester(context);
1587     QExplicitlySharedDataPointer<KDevelop::CompletionTreeItem> f;
1588 
1589     for (const auto& item : tester.items) {
1590         if (item->argumentHintDepth() == 1) {
1591             f = item;
1592             break;
1593         }
1594     }
1595 
1596     QVERIFY(f.data());
1597 
1598     const QString itemDisplay = tester.itemData(f).toString() + tester.itemData(f, KTextEditor:: CodeCompletionModel::Arguments).toString();
1599     QCOMPARE(itemDisplay, QStringLiteral("f(int i, int j = 0, double k = 1)"));
1600 }
1601 
1602 void TestCodeCompletion::testCompleteFunction()
1603 {
1604     QFETCH(QString, code);
1605     QFETCH(CompletionItems, expectedItems);
1606     QFETCH(QString, itemToExecute);
1607     QFETCH(QString, expectedCode);
1608 
1609     auto executeItem = [=] (const ClangCodeCompletionItemTester& tester) {
1610         auto item = tester.findItem(itemToExecute);
1611         QVERIFY(item);
1612         auto view = createView(tester.completionContext->duContext()->url().toUrl());
1613         DUChainReadLocker lock;
1614         item->execute(view.get(), view->document()->wordRangeAt(expectedItems.position));
1615         QCOMPARE(view->document()->text(), expectedCode);
1616     };
1617     executeCompletionTest(code, expectedItems, NoMacroOrBuiltin, executeItem);
1618 }
1619 
1620 void TestCodeCompletion::testCompleteFunction_data()
1621 {
1622     QTest::addColumn<QString>("code");
1623     QTest::addColumn<CompletionItems>("expectedItems");
1624     QTest::addColumn<QString>("itemToExecute");
1625     QTest::addColumn<QString>("expectedCode");
1626 
1627     QTest::newRow("add-parens")
1628         << "int foo();\nint main() {\n\n}"
1629         << CompletionItems({2, 0}, {"foo", "main"})
1630         << "foo"
1631         << "int foo();\nint main() {\nfoo()\n}";
1632 
1633     QTest::newRow("keep-parens")
1634         << "int foo();\nint main() {\nfoo();\n}"
1635         << CompletionItems({2, 0}, {"foo", "main"})
1636         << "main"
1637         << "int foo();\nint main() {\nmain();\n}";
1638 
1639     QTest::newRow("bug375635")
1640         << "enum class Color {\nBlue, Green, Red, Yellow\n};\nvoid foo() {\nColor x;\nswitch (x) {\ncase : break;}\n}"
1641         << CompletionItems({6, 5}, {"Blue", "Green", "Red", "Yellow"})
1642         << "Yellow"
1643         << "enum class Color {\nBlue, Green, Red, Yellow\n};\nvoid foo() {\nColor x;\nswitch (x) {\ncase Color::Yellow: break;}\n}";
1644 
1645     QTest::newRow("bug368544")
1646         << "class Klass {\npublic:\ntemplate <typename T>\nvoid func(int a, T x, int b) const;\n};\n"
1647         << CompletionItems({5, 0}, {"Klass", "Klass::func(int a, T x, int b) const"})
1648         << "Klass::func(int a, T x, int b) const"
1649         << "class Klass {\npublic:\ntemplate <typename T>\nvoid func(int a, T x, int b) const;\n};\ntemplate<typename T> void Klass::func(int a, T x, int b) const\n{\n}\n";
1650 
1651     QTest::newRow("bug377397")
1652         << "template<typename T> class Foo {\nvoid bar();\n};\n"
1653         << CompletionItems({3, 0}, {"Foo", "Foo<T>::bar()"})
1654         << "Foo<T>::bar()"
1655         << "template<typename T> class Foo {\nvoid bar();\n};\ntemplate<typename T> void Foo<T>::bar()\n{\n}\n";
1656 
1657     QTest::newRow("template-template-parameter")
1658         << "template <template<class, int> class X, typename B>\nclass Test {\npublic:\nvoid bar(B a);\n};\n"
1659         << CompletionItems({5, 0}, {"Test", "Test<X, B>::bar(B a)"})
1660         << "Test<X, B>::bar(B a)"
1661         << "template <template<class, int> class X, typename B>\nclass Test {\npublic:\nvoid bar(B a);\n};\ntemplate<template<typename, int> class X, typename B> void Test<X, B>::bar(B a)\n{\n}\n";
1662 
1663     QTest::newRow("replace-leading-return-type")
1664         << "void foo(int x);\nvoid "
1665         << CompletionItems({1, 5}, {"foo(int x)"})
1666         << "foo(int x)"
1667         << "void foo(int x);\nvoid foo(int x)\n{\n}\n";
1668 
1669     QTest::newRow("replace-leading-function-name")
1670         << "void foo(int x);\nfoo"
1671         << CompletionItems({1, 3}, {"foo(int x)"})
1672         << "foo(int x)"
1673         << "void foo(int x);\nvoid foo(int x)\n{\n}\n";
1674 
1675     QTest::newRow("replace-leading-with-class-method")
1676         << "class Foo { int bar(int x); };\nint "
1677         << CompletionItems({1, 4}, {"Foo", "Foo::bar(int x)"})
1678         << "Foo::bar(int x)"
1679         << "class Foo { int bar(int x); };\nint Foo::bar(int x)\n{\n}\n";
1680 
1681     QTest::newRow("replace-leading-whitespace-mismatch")
1682         << "class Foo { int** bar(int x); };\nint * *"
1683         << CompletionItems({1, 7}, {"** Foo::bar(int x)"})
1684         << "** Foo::bar(int x)"
1685         << "class Foo { int** bar(int x); };\nint ** Foo::bar(int x)\n{\n}\n";
1686 }
1687 
1688 void TestCodeCompletion::testIgnoreGccBuiltins()
1689 {
1690     // TODO: make it easier to change the compiler provider for testing purposes
1691     QTemporaryDir dir;
1692     auto project = new TestProject(Path(dir.path()), this);
1693     auto definesAndIncludesConfig = project->projectConfiguration()->group(definesAndIncludesConfigName);
1694     auto pathConfig = definesAndIncludesConfig.group("ProjectPath0");
1695     pathConfig.writeEntry("Path", ".");
1696     pathConfig.group("Compiler").writeEntry("Name", "GCC");
1697     m_projectController->addProject(project);
1698 
1699     {
1700         TestFile file(QString(), QStringLiteral("cpp"), project, dir.path());
1701 
1702         QVERIFY(file.parseAndWait(TopDUContext::AllDeclarationsContextsUsesAndAST));
1703 
1704         executeCompletionTest(file.topContext(), {});
1705     }
1706 }
1707 
1708 #include "moc_test_codecompletion.cpp"