From 9c4ba3ff21fc4691adb0017cd9ec92c567ba7bf8 Mon Sep 17 00:00:00 2001 From: Artem Sokolovskii Date: Wed, 8 Nov 2023 16:51:15 +0100 Subject: [PATCH] QuickFix: Fix add definition for templated function - Fix add definition for templated function in templated class - Fix "inline" placement for templated function Fixes: QTCREATORBUG-29408 Change-Id: I15f7793c9ae1e49d8338c1120135ddd1afbca4ca Reviewed-by: Christian Kandeler --- src/libs/cplusplus/TypePrettyPrinter.cpp | 92 +++++++------- src/plugins/cppeditor/cppquickfix_test.cpp | 137 +++++++++++++++++++++ src/plugins/cppeditor/cppquickfix_test.h | 6 + src/plugins/cppeditor/cppquickfixes.cpp | 27 +++- src/plugins/cppeditor/cppquickfixes.h | 1 + 5 files changed, 215 insertions(+), 48 deletions(-) diff --git a/src/libs/cplusplus/TypePrettyPrinter.cpp b/src/libs/cplusplus/TypePrettyPrinter.cpp index b563c4010e7..7ae1aa3397b 100644 --- a/src/libs/cplusplus/TypePrettyPrinter.cpp +++ b/src/libs/cplusplus/TypePrettyPrinter.cpp @@ -362,34 +362,38 @@ static bool endsWithPtrOrRef(const QString &type) void TypePrettyPrinter::visit(Function *type) { bool showTemplateParameters = _overview->showTemplateParameters; - QStringList nameParts = _name.split("::"); - int i = nameParts.length() - 1; - for (Scope *s = type->enclosingScope(); s && i >= 0; s = s->enclosingScope()) { - if (s->asClass()) - showTemplateParameters = true; - if (Template *templ = s->asTemplate(); templ && showTemplateParameters) { - QString &n = nameParts[i]; - const int paramCount = templ->templateParameterCount(); - if (paramCount > 0) { - n += '<'; - for (int index = 0; index < paramCount; ++index) { - if (index) - n += QLatin1String(", "); - QString arg = _overview->prettyName( - templ->templateParameterAt(index)->name()); - if (arg.isEmpty()) { - arg += 'T'; - arg += QString::number(index + 1); - } - n += arg; + QStringList nameParts = _name.split("::"); + int i = nameParts.length() - 1; + Scope *s = type->enclosingScope(); + if (s && s->asTemplate()) + s = s->enclosingScope(); + + for (; s && i >= 0; s = s->enclosingScope()) { + if (s->asClass()) + showTemplateParameters = true; + + if (Template *templ = s->asTemplate(); templ && showTemplateParameters) { + QString &n = nameParts[i]; + const int paramCount = templ->templateParameterCount(); + if (paramCount > 0) { + n += '<'; + for (int index = 0; index < paramCount; ++index) { + if (index) + n += QLatin1String(", "); + QString arg = _overview->prettyName(templ->templateParameterAt(index)->name()); + if (arg.isEmpty()) { + arg += 'T'; + arg += QString::number(index + 1); } - n += '>'; + n += arg; } + n += '>'; } - if (s->identifier()) - --i; + } else if (s->identifier()) { + --i; } - _name = nameParts.join("::"); + } + _name = nameParts.join("::"); if (_needsParens) { _text.prepend(QLatin1Char('(')); @@ -429,28 +433,30 @@ void TypePrettyPrinter::visit(Function *type) } if (_overview->showEnclosingTemplate) { - if (Template *templ = type->enclosingTemplate()) { - QString templateScope = "template<"; - const int paramCount = templ->templateParameterCount(); - for (int i = 0; i < paramCount; ++i) { - if (Symbol *param = templ->templateParameterAt(i)) { - if (i > 0) - templateScope.append(", "); - if (TypenameArgument *typenameArg = param->asTypenameArgument()) { - templateScope.append(QLatin1String(typenameArg->isClassDeclarator() - ? "class " : "typename ")); - QString name = _overview->prettyName(typenameArg->name()); - if (name.isEmpty()) - name.append('T').append(QString::number(i + 1)); - templateScope.append(name); - } else if (Argument *arg = param->asArgument()) { - templateScope.append(operator()(arg->type(), - _overview->prettyName(arg->name()))); + for (Scope *s = type->enclosingScope(); s && i >= 0; s = s->enclosingScope()) { + if (Template *templ = s->asTemplate()) { + QString templateScope = "template<"; + const int paramCount = templ->templateParameterCount(); + for (int i = 0; i < paramCount; ++i) { + if (Symbol *param = templ->templateParameterAt(i)) { + if (i > 0) + templateScope.append(", "); + if (TypenameArgument *typenameArg = param->asTypenameArgument()) { + templateScope.append(QLatin1String( + typenameArg->isClassDeclarator() ? "class " : "typename ")); + QString name = _overview->prettyName(typenameArg->name()); + if (name.isEmpty()) + name.append('T').append(QString::number(i + 1)); + templateScope.append(name); + } else if (Argument *arg = param->asArgument()) { + templateScope.append(operator()(arg->type(), + _overview->prettyName(arg->name()))); + } } } + if (paramCount > 0) + _text.prepend(templateScope + ">\n"); } - if (paramCount > 0) - _text.prepend(templateScope + ">\n"); } } diff --git a/src/plugins/cppeditor/cppquickfix_test.cpp b/src/plugins/cppeditor/cppquickfix_test.cpp index dc7346c6226..c656d2dd476 100644 --- a/src/plugins/cppeditor/cppquickfix_test.cpp +++ b/src/plugins/cppeditor/cppquickfix_test.cpp @@ -4999,6 +4999,34 @@ void QuickfixTest::testInsertDefFromDeclTemplateFunction() QuickFixOperationTest(singleDocument(original, expected), &factory); } +void QuickfixTest::testInsertDefFromDeclTemplateClassAndTemplateFunction() +{ + QByteArray original = + "template" + "class Foo\n" + "{\n" + " template\n" + " void fun@c();\n" + "};\n"; + QByteArray expected = + "template" + "class Foo\n" + "{\n" + " template\n" + " void fun@c();\n" + "};\n" + "\n" + "template\n" + "template\n" + "void Foo::func()\n" + "{\n" + "\n" + "}\n"; + + InsertDefFromDecl factory; + QuickFixOperationTest(singleDocument(original, expected), &factory); +} + void QuickfixTest::testInsertDefFromDeclFunctionWithSignedUnsignedArgument() { QByteArray original; @@ -5398,6 +5426,115 @@ SpaceBeforeParens: Always prefs->setCodeStyleSettings(settings); } +QList singleHeader(const QByteArray &original, const QByteArray &expected) +{ + return {CppTestDocument::create("file.h", original, expected)}; +} + +void QuickfixTest::testInsertDefOutsideFromDeclTemplateClassAndTemplateFunction() +{ + QByteArray original = + "template" + "class Foo\n" + "{\n" + " template\n" + " void fun@c();\n" + "};\n"; + QByteArray expected = + "template" + "class Foo\n" + "{\n" + " template\n" + " void fun@c();\n" + "};\n" + "\n" + "template\n" + "template\n" + "inline void Foo::func()\n" + "{\n" + "\n" + "}\n"; + + InsertDefFromDecl factory; + factory.m_defPosOutsideClass = true; + QuickFixOperationTest(singleHeader(original, expected), &factory); +} + +void QuickfixTest::testInsertDefOutsideFromDeclTemplateClass() +{ + QByteArray original = + "template" + "class Foo\n" + "{\n" + " void fun@c();\n" + "};\n"; + QByteArray expected = + "template" + "class Foo\n" + "{\n" + " void fun@c();\n" + "};\n" + "\n" + "template\n" + "inline void Foo::func()\n" + "{\n" + "\n" + "}\n"; + + InsertDefFromDecl factory; + factory.m_defPosOutsideClass = true; + QuickFixOperationTest(singleHeader(original, expected), &factory); +} + +void QuickfixTest::testInsertDefOutsideFromDeclTemplateFunction() +{ + QByteArray original = + "class Foo\n" + "{\n" + " template\n" + " void fun@c();\n" + "};\n"; + QByteArray expected = + "class Foo\n" + "{\n" + " template\n" + " void fun@c();\n" + "};\n" + "\n" + "template\n" + "inline void Foo::func()\n" + "{\n" + "\n" + "}\n"; + + InsertDefFromDecl factory; + factory.m_defPosOutsideClass = true; + QuickFixOperationTest(singleHeader(original, expected), &factory); +} + +void QuickfixTest::testInsertDefOutsideFromDeclFunction() +{ + QByteArray original = + "class Foo\n" + "{\n" + " void fun@c();\n" + "};\n"; + QByteArray expected = + "class Foo\n" + "{\n" + " void fun@c();\n" + "};\n" + "\n" + "inline void Foo::func()\n" + "{\n" + "\n" + "}\n"; + + InsertDefFromDecl factory; + factory.m_defPosOutsideClass = true; + QuickFixOperationTest(singleHeader(original, expected), &factory); +} + // Function for one of InsertDeclDef section cases void insertToSectionDeclFromDef(const QByteArray §ion, int sectionIndex) { diff --git a/src/plugins/cppeditor/cppquickfix_test.h b/src/plugins/cppeditor/cppquickfix_test.h index 99aff71696e..cc589f1908a 100644 --- a/src/plugins/cppeditor/cppquickfix_test.h +++ b/src/plugins/cppeditor/cppquickfix_test.h @@ -136,6 +136,7 @@ private slots: void testInsertDefFromDeclTemplateClass(); void testInsertDefFromDeclTemplateClassWithValueParam(); void testInsertDefFromDeclTemplateFunction(); + void testInsertDefFromDeclTemplateClassAndTemplateFunction(); void testInsertDefFromDeclFunctionWithSignedUnsignedArgument(); void testInsertDefFromDeclNotTriggeredForFriendFunc(); void testInsertDefFromDeclMinimalFunctionParameterType(); @@ -144,6 +145,11 @@ private slots: void testInsertDefsFromDecls(); void testInsertAndFormatDefsFromDecls(); + void testInsertDefOutsideFromDeclTemplateClassAndTemplateFunction(); + void testInsertDefOutsideFromDeclTemplateClass(); + void testInsertDefOutsideFromDeclTemplateFunction(); + void testInsertDefOutsideFromDeclFunction(); + void testInsertDeclFromDef(); void testInsertDeclFromDefTemplateFuncTypename(); void testInsertDeclFromDefTemplateFuncInt(); diff --git a/src/plugins/cppeditor/cppquickfixes.cpp b/src/plugins/cppeditor/cppquickfixes.cpp index 088449002ca..5128f7195ff 100644 --- a/src/plugins/cppeditor/cppquickfixes.cpp +++ b/src/plugins/cppeditor/cppquickfixes.cpp @@ -2782,10 +2782,27 @@ public: } const QString name = oo.prettyName(LookupContext::minimalName(decl, targetCoN, control)); - const QString defText = inlinePrefix( - targetFilePath, [defPos] { return defPos == DefPosOutsideClass; }) - + oo.prettyType(tn, name) - + QLatin1String("\n{\n\n}"); + + const QString inlinePref = inlinePrefix(targetFilePath, [defPos] { + return defPos == DefPosOutsideClass; + }); + + const QString prettyType = oo.prettyType(tn, name); + + QString input = prettyType; + int index = 0; + while (input.startsWith("template")) { + QRegularExpression templateRegex("template\\s*<[^>]*>"); + QRegularExpressionMatch match = templateRegex.match(input); + if (match.hasMatch()) { + index += match.captured().size() + 1; + input = input.mid(match.captured().size() + 1); + } + } + + QString defText = prettyType; + defText.insert(index, inlinePref); + defText += QLatin1String("\n{\n\n}"); const int targetPos = targetFile->position(loc.line(), loc.column()); const int targetPos2 = qMax(0, targetFile->position(loc.line(), 1) - 1); @@ -2901,7 +2918,7 @@ void InsertDefFromDecl::match(const CppQuickFixInterface &interface, QuickFixOpe const bool isFreeFunction = func->enclosingClass() == nullptr; // Insert Position: Outside Class - if (!isFreeFunction) { + if (!isFreeFunction || m_defPosOutsideClass) { result << new InsertDefOperation(interface, decl, declAST, InsertionLocation(), DefPosOutsideClass, diff --git a/src/plugins/cppeditor/cppquickfixes.h b/src/plugins/cppeditor/cppquickfixes.h index d4c038318b4..4598c2951bd 100644 --- a/src/plugins/cppeditor/cppquickfixes.h +++ b/src/plugins/cppeditor/cppquickfixes.h @@ -357,6 +357,7 @@ class InsertDefFromDecl: public CppQuickFixFactory { public: void match(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override; + bool m_defPosOutsideClass = false; }; class AddDeclarationForUndeclaredIdentifier : public CppQuickFixFactory