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 <christian.kandeler@qt.io>
This commit is contained in:
Artem Sokolovskii
2023-11-08 16:51:15 +01:00
parent ec99782105
commit 9c4ba3ff21
5 changed files with 215 additions and 48 deletions

View File

@@ -362,34 +362,38 @@ static bool endsWithPtrOrRef(const QString &type)
void TypePrettyPrinter::visit(Function *type) void TypePrettyPrinter::visit(Function *type)
{ {
bool showTemplateParameters = _overview->showTemplateParameters; bool showTemplateParameters = _overview->showTemplateParameters;
QStringList nameParts = _name.split("::"); QStringList nameParts = _name.split("::");
int i = nameParts.length() - 1; int i = nameParts.length() - 1;
for (Scope *s = type->enclosingScope(); s && i >= 0; s = s->enclosingScope()) { Scope *s = type->enclosingScope();
if (s->asClass()) if (s && s->asTemplate())
showTemplateParameters = true; s = s->enclosingScope();
if (Template *templ = s->asTemplate(); templ && showTemplateParameters) {
QString &n = nameParts[i]; for (; s && i >= 0; s = s->enclosingScope()) {
const int paramCount = templ->templateParameterCount(); if (s->asClass())
if (paramCount > 0) { showTemplateParameters = true;
n += '<';
for (int index = 0; index < paramCount; ++index) { if (Template *templ = s->asTemplate(); templ && showTemplateParameters) {
if (index) QString &n = nameParts[i];
n += QLatin1String(", "); const int paramCount = templ->templateParameterCount();
QString arg = _overview->prettyName( if (paramCount > 0) {
templ->templateParameterAt(index)->name()); n += '<';
if (arg.isEmpty()) { for (int index = 0; index < paramCount; ++index) {
arg += 'T'; if (index)
arg += QString::number(index + 1); n += QLatin1String(", ");
} QString arg = _overview->prettyName(templ->templateParameterAt(index)->name());
n += arg; if (arg.isEmpty()) {
arg += 'T';
arg += QString::number(index + 1);
} }
n += '>'; n += arg;
} }
n += '>';
} }
if (s->identifier()) } else if (s->identifier()) {
--i; --i;
} }
_name = nameParts.join("::"); }
_name = nameParts.join("::");
if (_needsParens) { if (_needsParens) {
_text.prepend(QLatin1Char('(')); _text.prepend(QLatin1Char('('));
@@ -429,28 +433,30 @@ void TypePrettyPrinter::visit(Function *type)
} }
if (_overview->showEnclosingTemplate) { if (_overview->showEnclosingTemplate) {
if (Template *templ = type->enclosingTemplate()) { for (Scope *s = type->enclosingScope(); s && i >= 0; s = s->enclosingScope()) {
QString templateScope = "template<"; if (Template *templ = s->asTemplate()) {
const int paramCount = templ->templateParameterCount(); QString templateScope = "template<";
for (int i = 0; i < paramCount; ++i) { const int paramCount = templ->templateParameterCount();
if (Symbol *param = templ->templateParameterAt(i)) { for (int i = 0; i < paramCount; ++i) {
if (i > 0) if (Symbol *param = templ->templateParameterAt(i)) {
templateScope.append(", "); if (i > 0)
if (TypenameArgument *typenameArg = param->asTypenameArgument()) { templateScope.append(", ");
templateScope.append(QLatin1String(typenameArg->isClassDeclarator() if (TypenameArgument *typenameArg = param->asTypenameArgument()) {
? "class " : "typename ")); templateScope.append(QLatin1String(
QString name = _overview->prettyName(typenameArg->name()); typenameArg->isClassDeclarator() ? "class " : "typename "));
if (name.isEmpty()) QString name = _overview->prettyName(typenameArg->name());
name.append('T').append(QString::number(i + 1)); if (name.isEmpty())
templateScope.append(name); name.append('T').append(QString::number(i + 1));
} else if (Argument *arg = param->asArgument()) { templateScope.append(name);
templateScope.append(operator()(arg->type(), } else if (Argument *arg = param->asArgument()) {
_overview->prettyName(arg->name()))); templateScope.append(operator()(arg->type(),
_overview->prettyName(arg->name())));
}
} }
} }
if (paramCount > 0)
_text.prepend(templateScope + ">\n");
} }
if (paramCount > 0)
_text.prepend(templateScope + ">\n");
} }
} }

View File

@@ -4999,6 +4999,34 @@ void QuickfixTest::testInsertDefFromDeclTemplateFunction()
QuickFixOperationTest(singleDocument(original, expected), &factory); QuickFixOperationTest(singleDocument(original, expected), &factory);
} }
void QuickfixTest::testInsertDefFromDeclTemplateClassAndTemplateFunction()
{
QByteArray original =
"template<class T>"
"class Foo\n"
"{\n"
" template<class U>\n"
" void fun@c();\n"
"};\n";
QByteArray expected =
"template<class T>"
"class Foo\n"
"{\n"
" template<class U>\n"
" void fun@c();\n"
"};\n"
"\n"
"template<class T>\n"
"template<class U>\n"
"void Foo<T>::func()\n"
"{\n"
"\n"
"}\n";
InsertDefFromDecl factory;
QuickFixOperationTest(singleDocument(original, expected), &factory);
}
void QuickfixTest::testInsertDefFromDeclFunctionWithSignedUnsignedArgument() void QuickfixTest::testInsertDefFromDeclFunctionWithSignedUnsignedArgument()
{ {
QByteArray original; QByteArray original;
@@ -5398,6 +5426,115 @@ SpaceBeforeParens: Always
prefs->setCodeStyleSettings(settings); prefs->setCodeStyleSettings(settings);
} }
QList<TestDocumentPtr> singleHeader(const QByteArray &original, const QByteArray &expected)
{
return {CppTestDocument::create("file.h", original, expected)};
}
void QuickfixTest::testInsertDefOutsideFromDeclTemplateClassAndTemplateFunction()
{
QByteArray original =
"template<class T>"
"class Foo\n"
"{\n"
" template<class U>\n"
" void fun@c();\n"
"};\n";
QByteArray expected =
"template<class T>"
"class Foo\n"
"{\n"
" template<class U>\n"
" void fun@c();\n"
"};\n"
"\n"
"template<class T>\n"
"template<class U>\n"
"inline void Foo<T>::func()\n"
"{\n"
"\n"
"}\n";
InsertDefFromDecl factory;
factory.m_defPosOutsideClass = true;
QuickFixOperationTest(singleHeader(original, expected), &factory);
}
void QuickfixTest::testInsertDefOutsideFromDeclTemplateClass()
{
QByteArray original =
"template<class T>"
"class Foo\n"
"{\n"
" void fun@c();\n"
"};\n";
QByteArray expected =
"template<class T>"
"class Foo\n"
"{\n"
" void fun@c();\n"
"};\n"
"\n"
"template<class T>\n"
"inline void Foo<T>::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<class U>\n"
" void fun@c();\n"
"};\n";
QByteArray expected =
"class Foo\n"
"{\n"
" template<class U>\n"
" void fun@c();\n"
"};\n"
"\n"
"template<class U>\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 // Function for one of InsertDeclDef section cases
void insertToSectionDeclFromDef(const QByteArray &section, int sectionIndex) void insertToSectionDeclFromDef(const QByteArray &section, int sectionIndex)
{ {

View File

@@ -136,6 +136,7 @@ private slots:
void testInsertDefFromDeclTemplateClass(); void testInsertDefFromDeclTemplateClass();
void testInsertDefFromDeclTemplateClassWithValueParam(); void testInsertDefFromDeclTemplateClassWithValueParam();
void testInsertDefFromDeclTemplateFunction(); void testInsertDefFromDeclTemplateFunction();
void testInsertDefFromDeclTemplateClassAndTemplateFunction();
void testInsertDefFromDeclFunctionWithSignedUnsignedArgument(); void testInsertDefFromDeclFunctionWithSignedUnsignedArgument();
void testInsertDefFromDeclNotTriggeredForFriendFunc(); void testInsertDefFromDeclNotTriggeredForFriendFunc();
void testInsertDefFromDeclMinimalFunctionParameterType(); void testInsertDefFromDeclMinimalFunctionParameterType();
@@ -144,6 +145,11 @@ private slots:
void testInsertDefsFromDecls(); void testInsertDefsFromDecls();
void testInsertAndFormatDefsFromDecls(); void testInsertAndFormatDefsFromDecls();
void testInsertDefOutsideFromDeclTemplateClassAndTemplateFunction();
void testInsertDefOutsideFromDeclTemplateClass();
void testInsertDefOutsideFromDeclTemplateFunction();
void testInsertDefOutsideFromDeclFunction();
void testInsertDeclFromDef(); void testInsertDeclFromDef();
void testInsertDeclFromDefTemplateFuncTypename(); void testInsertDeclFromDefTemplateFuncTypename();
void testInsertDeclFromDefTemplateFuncInt(); void testInsertDeclFromDefTemplateFuncInt();

View File

@@ -2782,10 +2782,27 @@ public:
} }
const QString name = oo.prettyName(LookupContext::minimalName(decl, targetCoN, const QString name = oo.prettyName(LookupContext::minimalName(decl, targetCoN,
control)); control));
const QString defText = inlinePrefix(
targetFilePath, [defPos] { return defPos == DefPosOutsideClass; }) const QString inlinePref = inlinePrefix(targetFilePath, [defPos] {
+ oo.prettyType(tn, name) return defPos == DefPosOutsideClass;
+ QLatin1String("\n{\n\n}"); });
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 targetPos = targetFile->position(loc.line(), loc.column());
const int targetPos2 = qMax(0, targetFile->position(loc.line(), 1) - 1); 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; const bool isFreeFunction = func->enclosingClass() == nullptr;
// Insert Position: Outside Class // Insert Position: Outside Class
if (!isFreeFunction) { if (!isFreeFunction || m_defPosOutsideClass) {
result << new InsertDefOperation(interface, decl, declAST, result << new InsertDefOperation(interface, decl, declAST,
InsertionLocation(), InsertionLocation(),
DefPosOutsideClass, DefPosOutsideClass,

View File

@@ -357,6 +357,7 @@ class InsertDefFromDecl: public CppQuickFixFactory
{ {
public: public:
void match(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override; void match(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override;
bool m_defPosOutsideClass = false;
}; };
class AddDeclarationForUndeclaredIdentifier : public CppQuickFixFactory class AddDeclarationForUndeclaredIdentifier : public CppQuickFixFactory