Members of the KDE Community are recommended to subscribe to the kde-community mailing list at https://mail.kde.org/mailman/listinfo/kde-community to allow them to participate in important discussions and receive other important announcements

Commit 221833e9 authored by Sergey Kalinichev's avatar Sergey Kalinichev

Attach correct declarations for overloaded code-completion items

Now function signature in completion item matches declaration signature.

REVIEW: 124001
parent 15a99289
......@@ -392,10 +392,10 @@ void addEnumItems(Declaration* declaration, QHash<QualifiedIdentifier, Declarati
}
}
QHash<QualifiedIdentifier, Declaration*> generateCache(const DUContextPointer& ctx, const CursorInRevision& position)
QMultiHash<QualifiedIdentifier, Declaration*> generateCache(const DUContextPointer& ctx, const CursorInRevision& position)
{
const auto allDeclarationsList = ctx->allDeclarations(position, ctx->topContext());
QHash<QualifiedIdentifier, Declaration*> declarationsHash;
QMultiHash<QualifiedIdentifier, Declaration*> declarationsHash;
for (const auto& declaration : allDeclarationsList) {
// We store function-local declarations with qid like: "function::declaration", but completion items provided by Clang have qid like: "declaration", so we use id here instead.
......@@ -409,6 +409,41 @@ QHash<QualifiedIdentifier, Declaration*> generateCache(const DUContextPointer& c
return declarationsHash;
}
struct FindDeclarationResult
{
Declaration* declaration = nullptr;
bool overloaded = false;
};
FindDeclarationResult findDeclaration(const QualifiedIdentifier& qid, const DUContextPointer& ctx, const CursorInRevision& position, const QMultiHash<QualifiedIdentifier, Declaration*>& declarationsCache, QSet<Declaration*>& handled)
{
FindDeclarationResult result;
auto i = declarationsCache.find(qid);
while (i != declarationsCache.end() && i.key() == qid) {
auto declaration = i.value();
if (!handled.contains(declaration)) {
handled.insert(declaration);
result.overloaded = result.overloaded || (++i != declarationsCache.end() && i.key() == qid);
result.declaration = declaration;
return result;
}
result.overloaded = true;
++i;
}
const auto foundDeclarations = ctx->findDeclarations(qid, position);
result.overloaded = foundDeclarations.size() > 1;
for (auto dec : foundDeclarations) {
if (!handled.contains(dec)) {
handled.insert(dec);
result.declaration = dec;
return result;
}
}
return result;
}
}
ClangCodeCompletionContext::ClangCodeCompletionContext(const DUContextPointer& context,
......@@ -603,22 +638,18 @@ QList<CompletionTreeItemPointer> ClangCodeCompletionContext::completionItems(boo
continue;
}
// TODO: This easily breaks if there are multiple function overloads
// e.g. void foo(), void foo(int) => only the first is selected
auto found = declarationsCache.value(qid);
if (!found) {
for (auto d : ctx->findDeclarations(qid, m_position)) {
if (!handled.contains(d)) {
found = d;
handled.insert(d);
break;
}
}
}
auto declResult = findDeclaration(qid, ctx, m_position, declarationsCache, handled);
auto found = declResult.declaration;
CompletionTreeItemPointer item;
if (found) {
if (declResult.overloaded && found->isFunctionDeclaration()) {
// In case of overloaded functions we can't use the "display" and the "resultType" provided by clang as it can mismatch declaration's data.
auto function = found->type<FunctionType>();
resultType = function->returnType()->toString();
display = id.toString() + function->partToString(FunctionType::SignatureArguments);
}
auto declarationItem = new DeclarationItem(found, display, resultType, replacement);
const unsigned int completionPriority = clang_getCompletionPriority(result.CompletionString);
......
......@@ -30,6 +30,7 @@
#include "util/clangtypes.h"
#include <language/codecompletion/codecompletiontesthelper.h>
#include <language/duchain/types/functiontype.h>
#include "codecompletion/completionhelper.h"
#include "codecompletion/context.h"
......@@ -564,3 +565,34 @@ void TestCodeCompletion::testIncludePathCompletionLocal()
QVERIFY(tester.names.contains(header.url().toUrl().fileName()));
QVERIFY(!tester.names.contains("iostream"));
}
void TestCodeCompletion::testOverloadedFunctions()
{
TestFile file("void f(); int f(int); void f(int, double){\n ", "cpp");
QVERIFY(file.parseAndWait(TopDUContext::AllDeclarationsContextsUsesAndAST));
DUChainReadLocker lock;
auto top = file.topContext();
QVERIFY(top);
const ParseSessionData::Ptr sessionData(dynamic_cast<ParseSessionData*>(top->ast().data()));
QVERIFY(sessionData);
DUContextPointer topPtr(top);
lock.unlock();
const auto context = new ClangCodeCompletionContext(topPtr, sessionData, file.url().toUrl(), {1, 0}, QString());
context->setFilters(ClangCodeCompletionContext::ContextFilters(
ClangCodeCompletionContext::NoBuiltins |
ClangCodeCompletionContext::NoMacros));
lock.lock();
const auto tester = ClangCodeCompletionItemTester(QExplicitlySharedDataPointer<ClangCodeCompletionContext>(context));
QCOMPARE(tester.items.size(), 3);
for (const auto& item : tester.items) {
auto function = item->declaration()->type<FunctionType>();
const QString display = item->declaration()->identifier().toString() + function->partToString(FunctionType::SignatureArguments);
QCOMPARE(display, tester.itemData(item).toString());
}
QVERIFY(tester.items[0]->declaration().data() != tester.items[1]->declaration().data());
QVERIFY(tester.items[0]->declaration().data() != tester.items[2]->declaration().data());
QVERIFY(tester.items[1]->declaration().data() != tester.items[2]->declaration().data());
}
......@@ -44,6 +44,8 @@ private slots:
void testImplement_data();
void testInvalidCompletions();
void testInvalidCompletions_data();
void testOverloadedFunctions();
};
#endif // TESTCODECOMPLETION_H
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment