aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Kandeler <[email protected]>2025-09-15 16:39:54 +0200
committerChristian Kandeler <[email protected]>2025-09-18 11:36:33 +0000
commit49d3004745eb11e73e65cdb862dad5a23f0b7c1e (patch)
treeef83b96eb775dd7275808906c71578163b5c1be4
parent46f91c445b7640fc67a4b9326a4d9c3b096b1b16 (diff)
CppEditor: Support parameter packs in refactoring operations
Fixes: QTCREATORBUG-32597 Change-Id: I8d6c9571f9a00ab36f09537a6be4a343b3fd9a84 Reviewed-by: Christian Stenger <[email protected]>
-rw-r--r--src/libs/3rdparty/cplusplus/Bind.cpp8
-rw-r--r--src/libs/3rdparty/cplusplus/Symbol.cpp7
-rw-r--r--src/libs/3rdparty/cplusplus/Symbol.h4
-rw-r--r--src/libs/cplusplus/TypePrettyPrinter.cpp84
-rw-r--r--src/libs/cplusplus/TypePrettyPrinter.h4
-rw-r--r--src/plugins/cppeditor/quickfixes/insertfunctiondefinition.cpp57
6 files changed, 131 insertions, 33 deletions
diff --git a/src/libs/3rdparty/cplusplus/Bind.cpp b/src/libs/3rdparty/cplusplus/Bind.cpp
index 8cca440b354..f5dbd1ea095 100644
--- a/src/libs/3rdparty/cplusplus/Bind.cpp
+++ b/src/libs/3rdparty/cplusplus/Bind.cpp
@@ -2480,6 +2480,8 @@ bool Bind::visit(ParameterDeclarationAST *ast)
Argument *arg = control()->newArgument(location(declaratorId, ast->firstToken()), argName);
arg->setType(type);
+ if (type && declaratorId && declaratorId->dot_dot_dot_token)
+ arg->setIsPack();
if (ast->expression)
arg->setInitializer(asStringLiteral(ast->expression));
@@ -2528,7 +2530,6 @@ bool Bind::visit(TemplateDeclarationAST *ast)
bool Bind::visit(TypenameTypeParameterAST *ast)
{
int sourceLocation = location(ast->name, ast->firstToken());
- // int dot_dot_dot_token = ast->dot_dot_dot_token;
const Name *name = this->name(ast->name);
ExpressionTy type_id = this->expression(ast->type_id);
CPlusPlus::Kind classKey = translationUnit()->tokenKind(ast->classkey_token);
@@ -2536,6 +2537,8 @@ bool Bind::visit(TypenameTypeParameterAST *ast)
TypenameArgument *arg = control()->newTypenameArgument(sourceLocation, name);
arg->setType(type_id);
arg->setClassDeclarator(classKey == T_CLASS);
+ if (ast->dot_dot_dot_token)
+ arg->setIsPack();
ast->symbol = arg;
_scope->addMember(arg);
return false;
@@ -2550,7 +2553,6 @@ bool Bind::visit(TemplateTypeParameterAST *ast)
// ### process the template prototype
// int greater_token = ast->greater_token;
- // int dot_dot_dot_token = ast->dot_dot_dot_token;
const Name *name = this->name(ast->name);
ExpressionTy type_id = this->expression(ast->type_id);
@@ -2575,6 +2577,8 @@ bool Bind::visit(TemplateTypeParameterAST *ast)
declaration(it->value);
}
arg->setClassDeclarator(translationUnit()->tokenKind(ast->class_token) == T_CLASS);
+ if (ast->dot_dot_dot_token)
+ arg->setIsPack();
ast->symbol = arg;
(void) switchScope(previousScope);
diff --git a/src/libs/3rdparty/cplusplus/Symbol.cpp b/src/libs/3rdparty/cplusplus/Symbol.cpp
index 4ed2ab0c720..6424b81cb58 100644
--- a/src/libs/3rdparty/cplusplus/Symbol.cpp
+++ b/src/libs/3rdparty/cplusplus/Symbol.cpp
@@ -102,7 +102,8 @@ Symbol::Symbol(TranslationUnit *translationUnit, int sourceLocation, const Name
_column(0),
_isGenerated(false),
_isDeprecated(false),
- _isUnavailable(false)
+ _isUnavailable(false),
+ _isPack(false)
{
setSourceLocation(sourceLocation, translationUnit);
setName(name);
@@ -122,7 +123,8 @@ Symbol::Symbol(Clone *clone, Subst *subst, Symbol *original)
_column(original->_column),
_isGenerated(original->_isGenerated),
_isDeprecated(original->_isDeprecated),
- _isUnavailable(original->_isUnavailable)
+ _isUnavailable(original->_isUnavailable),
+ _isPack(original->_isPack)
{
}
@@ -286,6 +288,7 @@ void Symbol::copy(Symbol *other)
_isGenerated = other->_isGenerated;
_isDeprecated = other->_isDeprecated;
+ _isPack = other->_isPack;
}
Link Symbol::toLink() const
diff --git a/src/libs/3rdparty/cplusplus/Symbol.h b/src/libs/3rdparty/cplusplus/Symbol.h
index d0076f7c33f..a3993b2caa4 100644
--- a/src/libs/3rdparty/cplusplus/Symbol.h
+++ b/src/libs/3rdparty/cplusplus/Symbol.h
@@ -256,6 +256,9 @@ public:
bool isUnavailable() const { return _isUnavailable; }
void setUnavailable(bool isUnavailable) { _isUnavailable = isUnavailable; }
+ bool isPack() const { return _isPack; }
+ void setIsPack() { _isPack = true; }
+
/// Returns this Symbol's eclosing scope.
Scope *enclosingScope() const { return _enclosingScope; }
@@ -305,6 +308,7 @@ private:
bool _isGenerated: 1;
bool _isDeprecated: 1;
bool _isUnavailable: 1;
+ bool _isPack: 1;
class HashCode;
diff --git a/src/libs/cplusplus/TypePrettyPrinter.cpp b/src/libs/cplusplus/TypePrettyPrinter.cpp
index 4c6e20dc2e2..8450bf37890 100644
--- a/src/libs/cplusplus/TypePrettyPrinter.cpp
+++ b/src/libs/cplusplus/TypePrettyPrinter.cpp
@@ -303,8 +303,10 @@ void TypePrettyPrinter::prependSpaceBeforeIndirection(const FullySpecifiedType &
_text.prepend(QLatin1Char(' '));
}
-QString TypePrettyPrinter::visitTemplateArgumentList(Scope *scope, int paramCount, bool forceNames)
+QString TypePrettyPrinter::visitTemplateArgumentList(Scope *scope, int paramCount, Context context,
+ bool forceNames)
{
+ const bool isDecl = context == Context::Declaration;
QString text = "<";
for (int i = 0; i < paramCount; ++i) {
Symbol * const param = scope->memberAt(i);
@@ -313,34 +315,66 @@ QString TypePrettyPrinter::visitTemplateArgumentList(Scope *scope, int paramCoun
if (i > 0)
text.append(", ");
if (TypenameArgument *arg = param->asTypenameArgument()) {
- text.append(arg->isClassDeclarator() ? "class" : "typename");
+ if (isDecl)
+ text.append(arg->isClassDeclarator() ? "class" : "typename");
QString name = _overview->prettyName(arg->name());
if (name.isEmpty() && forceNames)
name.append('T').append(QString::number(i + 1));
- if (!name.isEmpty())
- text.append(' ').append(name);
+ if (isDecl && arg->isPack())
+ text.append("...");
+ if (!name.isEmpty()) {
+ if (isDecl)
+ text.append(' ');
+ text.append(name);
+ }
+ if (!isDecl && arg->isPack())
+ text.append("...");
} else if (TemplateTypeArgument *arg = param->asTemplateTypeArgument()) {
if (arg->conceptName()) {
- text.append(_overview->prettyName(arg->conceptName()));
- if (arg->memberCount())
- text.append(visitTemplateArgumentList(arg, arg->memberCount(), false));
+ if (isDecl) {
+ text.append(_overview->prettyName(arg->conceptName()));
+ if (arg->memberCount()) {
+ text.append(visitTemplateArgumentList(
+ arg, arg->memberCount(), Context::Instantiation, false));
+ }
+ } else if (arg->name()) {
+ text.append(_overview->prettyName(arg->name()));
+ }
} else if (arg->memberCount()) {
- text.append(visitTemplateParameterList(arg, arg->memberCount(), false));
- text.append(arg->isClassDeclarator() ? " class" : " typename");
+ if (isDecl) {
+ text.append(visitTemplateParameterList(arg, arg->memberCount(), false));
+ text.append(arg->isClassDeclarator() ? " class" : " typename");
+ } else {
+ text.append(_overview->prettyName(arg->name()));
+ }
+ if (arg->isPack())
+ text.append("...");
}
- if (arg->name())
+ if (isDecl && arg->name())
text.append(' ').append(_overview->prettyName(arg->name()));
} else if (param->asArgument()) {
- text.append(_overview->prettyType(param->type(), param->name()));
+ if (isDecl) {
+ if (param->isPack()) {
+ text.append(_overview->prettyType(param->type()));
+ text.append("...");
+ if (param->name())
+ text.append(' ').append(_overview->prettyName(param->name()));
+ } else {
+ text.append(_overview->prettyType(param->type(), param->name()));
+ }
+ } else {
+ text.append(_overview->prettyName(param->name()));
+ if (param->isPack())
+ text.append("...");
+ }
}
}
return text.append('>');
-
}
QString TypePrettyPrinter::visitTemplateParameterList(Scope *scope, int paramCount, bool forceNames)
{
- return "template" + visitTemplateArgumentList(scope, paramCount, forceNames);
+ return "template" + visitTemplateArgumentList(scope, paramCount, Context::Declaration, forceNames);
}
void TypePrettyPrinter::prependSpaceAfterIndirection(bool hasName)
@@ -419,20 +453,8 @@ void TypePrettyPrinter::visit(Function *type)
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;
- }
- n += '>';
- }
+ if (paramCount)
+ n.append(visitTemplateArgumentList(templ, paramCount, Context::Instantiation, true));
} else if (s->identifier()) {
--i;
}
@@ -504,7 +526,13 @@ void TypePrettyPrinter::visit(Function *type)
if (_overview->showArgumentNames)
name = arg->name();
- _text += argOverview.prettyType(arg->type(), name);
+ if (arg->isPack()) {
+ _text.append(argOverview.prettyType(arg->type()).append("..."));
+ if (name)
+ _text.append(' ').append(argOverview.prettyName(name));
+ } else {
+ _text += argOverview.prettyType(arg->type(), name);
+ }
if (_overview->showDefaultArguments) {
if (const StringLiteral *initializer = arg->initializer()) {
diff --git a/src/libs/cplusplus/TypePrettyPrinter.h b/src/libs/cplusplus/TypePrettyPrinter.h
index f4f3db87a1f..00922ee3760 100644
--- a/src/libs/cplusplus/TypePrettyPrinter.h
+++ b/src/libs/cplusplus/TypePrettyPrinter.h
@@ -56,7 +56,9 @@ private:
void prependSpaceAfterIndirection(bool hasName);
void prependSpaceBeforeIndirection(const FullySpecifiedType &type);
- QString visitTemplateArgumentList(Scope *scope, int paramCount, bool forceNames);
+ enum class Context { Declaration, Instantiation };
+ QString visitTemplateArgumentList(Scope *scope, int paramCount, Context context,
+ bool forceNames);
QString visitTemplateParameterList(Scope *scope, int paramCount, bool forceNames);
enum IndirectionType { aPointerType, aReferenceType, aRvalueReferenceType };
diff --git a/src/plugins/cppeditor/quickfixes/insertfunctiondefinition.cpp b/src/plugins/cppeditor/quickfixes/insertfunctiondefinition.cpp
index 9afa1b21f05..4a8e6ca5fe5 100644
--- a/src/plugins/cppeditor/quickfixes/insertfunctiondefinition.cpp
+++ b/src/plugins/cppeditor/quickfixes/insertfunctiondefinition.cpp
@@ -2268,6 +2268,63 @@ foo::foo2::MyType<int> foo::foo2::bar()
InsertDefFromDecl factory;
QuickFixOperationTest(singleDocument(original, expected), &factory);
}
+
+ void testParameterPack()
+ {
+ const QByteArray original =
+ "template<typename... Args> struct Foo {\n"
+ " explicit @Foo(Args &&... args);\n"
+ "};\n";
+ const QByteArray expected =
+ "template<typename... Args> struct Foo {\n"
+ " explicit Foo(Args &&... args);\n"
+ "};\n\n"
+ "template<typename... Args>\n"
+ "Foo<Args...>::Foo(Args &&... args)\n"
+ "{\n\n"
+ "}\n";
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+
+ void testConstantParameterPack()
+ {
+ const QByteArray original =
+ "template<int... Args> struct Foo {\n"
+ " void @foo();\n"
+ "};\n";
+ const QByteArray expected =
+ "template<int... Args> struct Foo {\n"
+ " void foo();\n"
+ "};\n\n"
+ "template<int... Args>\n"
+ "void Foo<Args...>::foo()\n"
+ "{\n\n"
+ "}\n";
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+
+ void testTemplateTemplateParameterPack()
+ {
+ const QByteArray original =
+ "template<template<typename...> class... C> struct Foo {\n"
+ " void @foo(C<int>... c);\n"
+ "};\n";
+ const QByteArray expected =
+ "template<template<typename...> class... C> struct Foo {\n"
+ " void foo(C<int>... c);\n"
+ "};\n\n"
+ "template<template<typename...> class... C>\n"
+ "void Foo<C...>::foo(C<int>... c)\n"
+ "{\n\n"
+ "}\n";
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
};
class InsertDefsFromDeclsTest : public QObject