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 @@ -55,12 +55,33 @@ QString templatePrefix; }; +QStringList templateParams(CXCursor cursor); + CXChildVisitResult templateParamsHelper(CXCursor cursor, CXCursor /*parent*/, CXClientData data) { CXCursorKind kind = clang_getCursorKind(cursor); - if (kind == CXCursor_TemplateTypeParameter || kind == CXCursor_TemplateTemplateParameter || - kind == CXCursor_NonTypeTemplateParameter) { - (*static_cast(data)).append(ClangString(clang_getCursorSpelling(cursor)).toString()); + auto& params = *static_cast(data); + if (kind == CXCursor_TemplateTypeParameter) { + auto paramName = ClangString(clang_getCursorSpelling(cursor)).toString(); + auto param = QStringLiteral("typename"); + if (!paramName.isEmpty()) { + param += QLatin1Char(' ') + paramName; + } + params.append(param); + } + else if (kind == CXCursor_NonTypeTemplateParameter) { + auto param = ClangString(clang_getTypeSpelling(clang_getCursorType(cursor))).toString(); + auto paramName = ClangString(clang_getCursorSpelling(cursor)).toString(); + if (!paramName.isEmpty()) { + param += QLatin1Char(' ') + paramName; + } + params.append(param); + } + else if (kind == CXCursor_TemplateTemplateParameter) { + auto paramName = ClangString(clang_getCursorSpelling(cursor)).toString(); + auto templateTypes = templateParams(cursor); + auto param = QLatin1String("template<") + templateTypes.join(QStringLiteral(", ")) + QLatin1String("> class ") + paramName ; + params.append(param); } return CXChildVisit_Continue; } @@ -247,7 +268,7 @@ //which goes at the front of the prototype const QStringList templateTypes = templateParams(kind == CXCursor_ClassTemplate ? cursor : clang_getSpecializedCursorTemplate(cursor)); - templatePrefix = QLatin1String("template<") + templateTypes.join(QStringLiteral(", typename ")) + QLatin1String("> "); + templatePrefix = QLatin1String("template<") + templateTypes.join(QStringLiteral(", ")) + QLatin1String("> "); } } @@ -292,6 +313,12 @@ return CXChildVisit_Continue; } + QString templatePrefix; + if (kind == CXCursor_FunctionTemplate) { + const QStringList templateTypes = templateParams(cursor); + templatePrefix = QLatin1String("template<") + templateTypes.join(QStringLiteral(", ")) + QLatin1String("> "); + } + const auto scope = ClangUtils::getScope(cursor, data->origin); QString signature = ClangUtils::getCursorSignature(cursor, scope); @@ -313,7 +340,7 @@ } DeclarationPointer declaration = ClangHelpers::findDeclaration(clang_getCursorLocation(cursor), QualifiedIdentifier(), top); data->prototypes->append(FuncImplementInfo{kind == CXCursor_Constructor, kind == CXCursor_Destructor, - data->templatePrefix, returnType, rest, declaration}); + data->templatePrefix + templatePrefix, returnType, rest, declaration}); return CXChildVisit_Continue; } 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 @@ -831,6 +831,16 @@ )" << CompletionItems{{5, 0}, {"Hello::bar()"}}; + QTest::newRow("bug368544") + << R"( + class Klass { + public: + template + void func(int a, T x, int b) const; + }; + )" + << CompletionItems{{6, 0}, {"Klass::func(int a, T x, int b) const"}}; + } void TestCodeCompletion::testImplementOtherFile() @@ -1360,6 +1370,24 @@ << CompletionItems({6, 5}, {"Blue", "Green", "Red", "Yellow"}) << "Yellow" << "enum class Color {\nBlue, Green, Red, Yellow\n};\nvoid foo() {\nColor x;\nswitch (x) {\ncase Color::Yellow: break;}\n}"; + + QTest::newRow("bug368544") + << "class Klass {\npublic:\ntemplate \nvoid func(int a, T x, int b) const;\n};\n" + << CompletionItems({5, 0}, {"Klass", "Klass::func(int a, T x, int b) const"}) + << "Klass::func(int a, T x, int b) const" + << "class Klass {\npublic:\ntemplate \nvoid func(int a, T x, int b) const;\n};\ntemplate void Klass::func(int a, T x, int b) const\n{\n}\n"; + + QTest::newRow("bug377397") + << "template class Foo {\nvoid bar();\n};\n" + << CompletionItems({3, 0}, {"Foo", "Foo::bar()"}) + << "Foo::bar()" + << "template class Foo {\nvoid bar();\n};\ntemplate void Foo::bar()\n{\n}\n"; + + QTest::newRow("template-template-parameter") + << "template class X, typename B>\nclass Test {\npublic:\nvoid bar(B a);\n};\n" + << CompletionItems({5, 0}, {"Test", "Test::bar(B a)"}) + << "Test::bar(B a)" + << "template class X, typename B>\nclass Test {\npublic:\nvoid bar(B a);\n};\ntemplate class X, typename B> void Test::bar(B a)\n{\n}\n"; } void TestCodeCompletion::testIgnoreGccBuiltins() 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 @@ -244,9 +244,34 @@ //Add the parameters and such stream << '('; - int numArgs = clang_Cursor_getNumArguments(cursor); + int numArgs ; + QVector args; + + // SEE https://bugs.kde.org/show_bug.cgi?id=368544 + // clang_Cursor_getNumArguments returns -1 for FunctionTemplate + // clang checks if cursor's Decl is ObjCMethodDecl or FunctionDecl + // CXCursor_FunctionTemplate is neither of them instead it has a FunctionTemplateDecl + // HACK Get function template arguments by visiting children + if (kind == CXCursor_FunctionTemplate) { + clang_visitChildren(cursor, [] (CXCursor cursor, CXCursor /*parent*/, CXClientData data) { + if (clang_getCursorKind(cursor) == CXCursor_ParmDecl) { + (static_cast*>(data))->push_back(cursor); + } + return CXChildVisit_Continue; + }, &args); + numArgs = args.size(); + } + else { + numArgs = clang_Cursor_getNumArguments(cursor); + args.reserve(numArgs); + for (int i = 0; i < numArgs; i++) { + CXCursor arg = clang_Cursor_getArgument(cursor, i); + args.push_back(arg); + } + } + for (int i = 0; i < numArgs; i++) { - CXCursor arg = clang_Cursor_getArgument(cursor, i); + CXCursor arg = args[i]; //Clang formats pointer types as "t *x" and reference types as "t &x", while //KDevelop formats them as "t* x" and "t& x". Make that adjustment.