diff --git a/plugins/clang/codecompletion/completionhelper.cpp b/plugins/clang/codecompletion/completionhelper.cpp --- a/plugins/clang/codecompletion/completionhelper.cpp +++ b/plugins/clang/codecompletion/completionhelper.cpp @@ -50,11 +50,17 @@ { CXCursor origin; CXCursor top; - FunctionImplementsList* prototypes; QVector originScope; QVector fileFilter; int depth; QString templatePrefix; + QVector& qualifiedNamespaces; + struct PendingFunctions + { + CXCursor cursor; + QString templatePrefix; + }; + QVector& pendingFunctions; }; QStringList templateParams(CXCursor cursor); @@ -272,10 +278,12 @@ } } - ImplementsInfo info{data->origin, data->top, data->prototypes, data->originScope, + ImplementsInfo info{data->origin, data->top, data->originScope, data->fileFilter, data->depth + 1, - data->templatePrefix + templatePrefix}; + data->templatePrefix + templatePrefix, + data->qualifiedNamespaces, + data->pendingFunctions}; clang_visitChildren(cursor, declVisitor, &info); return CXChildVisit_Continue; @@ -285,6 +293,11 @@ return CXChildVisit_Continue; } + if (kind == CXCursor_UsingDirective) { + data->qualifiedNamespaces.append(clang_getCursorDefinition(cursor)); + return CXChildVisit_Continue; + } + //If the current cursor is not a function or if it is already defined, there's nothing to do here if (!CursorKindTraits::isFunction(clang_getCursorKind(cursor)) || !clang_equalCursors(clang_getNullCursor(), clang_getCursorDefinition(cursor))) @@ -319,30 +332,44 @@ templatePrefix = QLatin1String("template<") + templateTypes.join(QStringLiteral(", ")) + QLatin1String("> "); } - const auto scope = ClangUtils::getScope(cursor, data->origin); - QString signature = ClangUtils::getCursorSignature(cursor, scope); + data->pendingFunctions.append({cursor, data->templatePrefix + templatePrefix}); - QString returnType, rest; - if (kind != CXCursor_Constructor && kind != CXCursor_Destructor) { - int spaceIndex = signature.indexOf(QLatin1Char(' ')); - returnType = signature.left(spaceIndex); - rest = signature.mid(spaceIndex + 1); - } else { - rest = signature; - } + return CXChildVisit_Continue; +} - //TODO Add support for pure virtual functions +void processImplements(const ImplementsInfo& info, FunctionImplementsList& implements) +{ + for (const auto& pendingFunction : info.pendingFunctions) { + const CXCursor cursor = pendingFunction.cursor; + const CXCursorKind kind = clang_getCursorKind(cursor); + + const auto scope = ClangUtils::getScope(cursor, info.origin, info.qualifiedNamespaces); + QString signature = ClangUtils::getCursorSignature(cursor, scope); + + QString returnType, rest; + if (kind != CXCursor_Constructor && kind != CXCursor_Destructor) { + int spaceIndex = signature.indexOf(QLatin1Char(' ')); + returnType = signature.left(spaceIndex); + rest = signature.mid(spaceIndex + 1); + } else { + rest = signature; + } - ReferencedTopDUContext top; - { - DUChainReadLocker lock; - top = DUChain::self()->chainForDocument(ClangString(clang_getFileName(file)).toIndexed()); - } - DeclarationPointer declaration = ClangHelpers::findDeclaration(clang_getCursorLocation(cursor), QualifiedIdentifier(), top); - data->prototypes->append(FuncImplementInfo{kind == CXCursor_Constructor, kind == CXCursor_Destructor, - data->templatePrefix + templatePrefix, returnType, rest, declaration}); + //TODO Add support for pure virtual functions - return CXChildVisit_Continue; + ReferencedTopDUContext top; + { + CXFile file = nullptr; + auto location = clang_getCursorLocation(cursor); + clang_getFileLocation(location, &file, nullptr, nullptr, nullptr); + + DUChainReadLocker lock; + top = DUChain::self()->chainForDocument(ClangString(clang_getFileName(file)).toIndexed()); + } + DeclarationPointer declaration = ClangHelpers::findDeclaration(clang_getCursorLocation(cursor), QualifiedIdentifier(), top); + implements.append(FuncImplementInfo{kind == CXCursor_Constructor, kind == CXCursor_Destructor, + pendingFunction.templatePrefix, returnType, rest, declaration}); + } } } @@ -403,8 +430,11 @@ } } - ImplementsInfo info{currentCursor, topCursor, &m_implements, scopes, fileFilter, 0, QString()}; + QVector qualifiedNamespaces; + QVector pendingFunctions; + ImplementsInfo info{currentCursor, topCursor, scopes, fileFilter, 0, QString(), qualifiedNamespaces, pendingFunctions}; clang_visitChildren(topCursor, declVisitor, &info); + processImplements(info, m_implements); } } diff --git a/plugins/clang/tests/test_codecompletion.cpp b/plugins/clang/tests/test_codecompletion.cpp --- a/plugins/clang/tests/test_codecompletion.cpp +++ b/plugins/clang/tests/test_codecompletion.cpp @@ -841,6 +841,18 @@ )" << CompletionItems{{6, 0}, {"Klass::func(int a, T x, int b) const"}}; + QTest::newRow("bug373722-using-directive") + << R"( + namespace A { + namespace B { + namespace C { + void foo(); + } + } + } + using namespace A::B; + )" + << CompletionItems{{9, 0}, {"C::foo()"}}; } void TestCodeCompletion::testImplementOtherFile() diff --git a/plugins/clang/util/clangutils.h b/plugins/clang/util/clangutils.h --- a/plugins/clang/util/clangutils.h +++ b/plugins/clang/util/clangutils.h @@ -87,9 +87,11 @@ * @param cursor The cursor to examine * @param context The destination context from which the cursor should be referenced. * By default this will be set to the cursors lexical parent. + * @param qualified List of namespaces explicitly qualified with a using-directive. + * By default, empty. * @return the cursor's scope as a string */ - KDEVCLANGPRIVATE_EXPORT QString getScope(CXCursor cursor, CXCursor context = clang_getNullCursor()); + KDEVCLANGPRIVATE_EXPORT QString getScope(CXCursor cursor, CXCursor context = clang_getNullCursor(), const QVector& qualified = {}); /** * Given a cursor representing some sort of function, returns its signature. The diff --git a/plugins/clang/util/clangutils.cpp b/plugins/clang/util/clangutils.cpp --- a/plugins/clang/util/clangutils.cpp +++ b/plugins/clang/util/clangutils.cpp @@ -204,15 +204,16 @@ kind == CXCursor_ClassTemplate || kind == CXCursor_ClassTemplatePartialSpecialization; } -QString ClangUtils::getScope(CXCursor cursor, CXCursor context) +QString ClangUtils::getScope(CXCursor cursor, CXCursor context, const QVector& qualified) { QStringList scope; if (clang_Cursor_isNull(context)) { context = clang_getCursorLexicalParent(cursor); } context = clang_getCanonicalCursor(context); CXCursor search = clang_getCursorSemanticParent(cursor); - while (isScopeKind(clang_getCursorKind(search)) && !clang_equalCursors(search, context)) { + while (isScopeKind(clang_getCursorKind(search)) && !clang_equalCursors(search, context) + && std::none_of(qualified.begin(), qualified.end(), [search](CXCursor ns) { return clang_equalCursors(search, ns); })) { scope.prepend(ClangString(clang_getCursorDisplayName(search)).toString()); search = clang_getCursorSemanticParent(search); }