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"