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,32 @@ 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()); + if (kind == CXCursor_TemplateTypeParameter) { + auto paramName = ClangString(clang_getCursorSpelling(cursor)).toString(); + auto param = QStringLiteral("typename"); + if(!paramName.isEmpty()) { + param += QStringLiteral(" ") + paramName; + } + (*static_cast(data)).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 += QStringLiteral(" ") + paramName; + } + (*static_cast(data)).append(param); + } + else if (kind == CXCursor_TemplateTemplateParameter) { + QString paramName = ClangString(clang_getCursorSpelling(cursor)).toString(); + auto templateTypes = templateParams(cursor); + QString param = QLatin1String("template<") + templateTypes.join(QStringLiteral(", ")) + QLatin1String("> class ") + paramName ; + (*static_cast(data)).append(param); } return CXChildVisit_Continue; } @@ -247,7 +267,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("> "); } } @@ -291,6 +311,12 @@ if (isQtMocFunction(cursor) || ClangUtils::specialAttributes(cursor) & FunctionSignalFlag) { 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 +339,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 @@ -830,6 +830,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"}}; } @@ -1354,6 +1364,25 @@ << CompletionItems({2, 0}, {"foo", "main"}) << "main" << "int foo();\nint main() {\nmain();\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 @@ -148,6 +148,14 @@ return CXChildVisit_Continue; } +CXChildVisitResult getFunctionTemplateParameters(CXCursor cursor, CXCursor /*parent*/, CXClientData data) +{ + if(clang_getCursorKind(cursor) == CXCursor_ParmDecl) { + (static_cast*>(data))->push_back(cursor); + } + return CXChildVisit_Continue; +} + } QVector ClangUtils::getDefaultArguments(CXCursor cursor, DefaultArgumentsMode mode) @@ -244,9 +252,27 @@ //Add the parameters and such stream << '('; - int numArgs = clang_Cursor_getNumArguments(cursor); + int numArgs ; + QVector args; + + // 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 + if(kind != CXCursor_FunctionTemplate) { + 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); + } + } + else { + clang_visitChildren(cursor, getFunctionTemplateParameters, &args); + numArgs = args.size(); + } + 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.