diff options
author | Christian Kandeler <[email protected]> | 2025-09-15 16:39:54 +0200 |
---|---|---|
committer | Christian Kandeler <[email protected]> | 2025-09-18 11:36:33 +0000 |
commit | 49d3004745eb11e73e65cdb862dad5a23f0b7c1e (patch) | |
tree | ef83b96eb775dd7275808906c71578163b5c1be4 | |
parent | 46f91c445b7640fc67a4b9326a4d9c3b096b1b16 (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.cpp | 8 | ||||
-rw-r--r-- | src/libs/3rdparty/cplusplus/Symbol.cpp | 7 | ||||
-rw-r--r-- | src/libs/3rdparty/cplusplus/Symbol.h | 4 | ||||
-rw-r--r-- | src/libs/cplusplus/TypePrettyPrinter.cpp | 84 | ||||
-rw-r--r-- | src/libs/cplusplus/TypePrettyPrinter.h | 4 | ||||
-rw-r--r-- | src/plugins/cppeditor/quickfixes/insertfunctiondefinition.cpp | 57 |
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 |