diff --git a/src/plugins/cppeditor/CMakeLists.txt b/src/plugins/cppeditor/CMakeLists.txt index cf652940e92..d363fcdb97e 100644 --- a/src/plugins/cppeditor/CMakeLists.txt +++ b/src/plugins/cppeditor/CMakeLists.txt @@ -109,6 +109,7 @@ add_qtc_plugin(CppEditor quickfixes/cppquickfixsettings.cpp quickfixes/cppquickfixsettings.h quickfixes/cppquickfixsettingspage.cpp quickfixes/cppquickfixsettingspage.h quickfixes/cppquickfixsettingswidget.cpp quickfixes/cppquickfixsettingswidget.h + quickfixes/convertfromandtopointer.cpp quickfixes/convertfromandtopointer.h quickfixes/createdeclarationfromuse.cpp quickfixes/createdeclarationfromuse.h quickfixes/extractfunction.cpp quickfixes/extractfunction.h quickfixes/extractliteralasparameter.cpp quickfixes/extractliteralasparameter.h diff --git a/src/plugins/cppeditor/cppeditor.qbs b/src/plugins/cppeditor/cppeditor.qbs index 4685bc13fb0..cd70cc6929e 100644 --- a/src/plugins/cppeditor/cppeditor.qbs +++ b/src/plugins/cppeditor/cppeditor.qbs @@ -221,6 +221,8 @@ QtcPlugin { files: [ "bringidentifierintoscope.cpp", "bringidentifierintoscope.h", + "convertfromandtopointer.cpp", + "convertfromandtopointer.h", "convertqt4connect.cpp", "convertqt4connect.h", "convertstringliteral.cpp", diff --git a/src/plugins/cppeditor/quickfixes/convertfromandtopointer.cpp b/src/plugins/cppeditor/quickfixes/convertfromandtopointer.cpp new file mode 100644 index 00000000000..3b5345f6881 --- /dev/null +++ b/src/plugins/cppeditor/quickfixes/convertfromandtopointer.cpp @@ -0,0 +1,705 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "convertfromandtopointer.h" + +#include "../cppeditortr.h" +#include "../cpprefactoringchanges.h" +#include "cppquickfix.h" + +#include +#include +#include + +#ifdef WITH_TESTS +#include "cppquickfix_test.h" +#include +#endif + +using namespace CPlusPlus; +using namespace Utils; + +namespace CppEditor::Internal { +namespace { + +class ConvertFromAndToPointerOp : public CppQuickFixOperation +{ +public: + enum Mode { FromPointer, FromVariable, FromReference }; + + ConvertFromAndToPointerOp(const CppQuickFixInterface &interface, int priority, Mode mode, + bool isAutoDeclaration, + const SimpleDeclarationAST *simpleDeclaration, + const DeclaratorAST *declaratorAST, + const SimpleNameAST *identifierAST, + Symbol *symbol) + : CppQuickFixOperation(interface, priority) + , m_mode(mode) + , m_isAutoDeclaration(isAutoDeclaration) + , m_simpleDeclaration(simpleDeclaration) + , m_declaratorAST(declaratorAST) + , m_identifierAST(identifierAST) + , m_symbol(symbol) + , m_refactoring(snapshot()) + , m_file(m_refactoring.cppFile(filePath())) + , m_document(interface.semanticInfo().doc) + { + setDescription( + mode == FromPointer + ? Tr::tr("Convert to Stack Variable") + : Tr::tr("Convert to Pointer")); + } + + void perform() override + { + ChangeSet changes; + + switch (m_mode) { + case FromPointer: + removePointerOperator(changes); + convertToStackVariable(changes); + break; + case FromReference: + removeReferenceOperator(changes); + Q_FALLTHROUGH(); + case FromVariable: + convertToPointer(changes); + break; + } + + m_file->setChangeSet(changes); + m_file->apply(); + } + +private: + void removePointerOperator(ChangeSet &changes) const + { + if (!m_declaratorAST->ptr_operator_list) + return; + PointerAST *ptrAST = m_declaratorAST->ptr_operator_list->value->asPointer(); + QTC_ASSERT(ptrAST, return); + const int pos = m_file->startOf(ptrAST->star_token); + changes.remove(pos, pos + 1); + } + + void removeReferenceOperator(ChangeSet &changes) const + { + ReferenceAST *refAST = m_declaratorAST->ptr_operator_list->value->asReference(); + QTC_ASSERT(refAST, return); + const int pos = m_file->startOf(refAST->reference_token); + changes.remove(pos, pos + 1); + } + + void removeNewExpression(ChangeSet &changes, NewExpressionAST *newExprAST) const + { + ExpressionListAST *exprlist = nullptr; + if (newExprAST->new_initializer) { + if (ExpressionListParenAST *ast = newExprAST->new_initializer->asExpressionListParen()) + exprlist = ast->expression_list; + else if (BracedInitializerAST *ast = newExprAST->new_initializer->asBracedInitializer()) + exprlist = ast->expression_list; + } + + if (exprlist) { + // remove 'new' keyword and type before initializer + changes.remove(m_file->startOf(newExprAST->new_token), + m_file->startOf(newExprAST->new_initializer)); + + changes.remove(m_file->endOf(m_declaratorAST->equal_token - 1), + m_file->startOf(m_declaratorAST->equal_token + 1)); + } else { + // remove the whole new expression + changes.remove(m_file->endOf(m_identifierAST->firstToken()), + m_file->startOf(newExprAST->lastToken())); + } + } + + void removeNewKeyword(ChangeSet &changes, NewExpressionAST *newExprAST) const + { + // remove 'new' keyword before initializer + changes.remove(m_file->startOf(newExprAST->new_token), + m_file->startOf(newExprAST->new_type_id)); + } + + void convertToStackVariable(ChangeSet &changes) const + { + // Handle the initializer. + if (m_declaratorAST->initializer) { + if (NewExpressionAST *newExpression = m_declaratorAST->initializer->asNewExpression()) { + if (m_isAutoDeclaration) { + if (!newExpression->new_initializer) + changes.insert(m_file->endOf(newExpression), QStringLiteral("()")); + removeNewKeyword(changes, newExpression); + } else { + removeNewExpression(changes, newExpression); + } + } + } + + // Fix all occurrences of the identifier in this function. + ASTPath astPath(m_document); + const QList uses = semanticInfo().localUses.value(m_symbol); + for (const SemanticInfo::Use &use : uses) { + const QList path = astPath(use.line, use.column); + AST *idAST = path.last(); + bool declarationFound = false; + bool starFound = false; + int ampersandPos = 0; + bool memberAccess = false; + bool deleteCall = false; + + for (int i = path.count() - 2; i >= 0; --i) { + if (path.at(i) == m_declaratorAST) { + declarationFound = true; + break; + } + if (MemberAccessAST *memberAccessAST = path.at(i)->asMemberAccess()) { + if (m_file->tokenAt(memberAccessAST->access_token).kind() != T_ARROW) + continue; + int pos = m_file->startOf(memberAccessAST->access_token); + changes.replace(pos, pos + 2, QLatin1String(".")); + memberAccess = true; + break; + } else if (DeleteExpressionAST *deleteAST = path.at(i)->asDeleteExpression()) { + const int pos = m_file->startOf(deleteAST->delete_token); + changes.insert(pos, QLatin1String("// ")); + deleteCall = true; + break; + } else if (UnaryExpressionAST *unaryExprAST = path.at(i)->asUnaryExpression()) { + const Token tk = m_file->tokenAt(unaryExprAST->unary_op_token); + if (tk.kind() == T_STAR) { + if (!starFound) { + int pos = m_file->startOf(unaryExprAST->unary_op_token); + changes.remove(pos, pos + 1); + } + starFound = true; + } else if (tk.kind() == T_AMPER) { + ampersandPos = m_file->startOf(unaryExprAST->unary_op_token); + } + } else if (PointerAST *ptrAST = path.at(i)->asPointer()) { + if (!starFound) { + const int pos = m_file->startOf(ptrAST->star_token); + changes.remove(pos, pos); + } + starFound = true; + } else if (path.at(i)->asFunctionDefinition()) { + break; + } + } + if (!declarationFound && !starFound && !memberAccess && !deleteCall) { + if (ampersandPos) { + changes.insert(ampersandPos, QLatin1String("&(")); + changes.insert(m_file->endOf(idAST->firstToken()), QLatin1String(")")); + } else { + changes.insert(m_file->startOf(idAST), QLatin1String("&")); + } + } + } + } + + QString typeNameOfDeclaration() const + { + if (!m_simpleDeclaration + || !m_simpleDeclaration->decl_specifier_list + || !m_simpleDeclaration->decl_specifier_list->value) { + return QString(); + } + NamedTypeSpecifierAST *namedType + = m_simpleDeclaration->decl_specifier_list->value->asNamedTypeSpecifier(); + if (!namedType) + return QString(); + + Overview overview; + return overview.prettyName(namedType->name->name); + } + + void insertNewExpression(ChangeSet &changes, ExpressionAST *ast) const + { + const QString typeName = typeNameOfDeclaration(); + if (CallAST *callAST = ast->asCall()) { + if (typeName.isEmpty()) { + changes.insert(m_file->startOf(callAST), QLatin1String("new ")); + } else { + changes.insert(m_file->startOf(callAST), + QLatin1String("new ") + typeName + QLatin1Char('(')); + changes.insert(m_file->startOf(callAST->lastToken()), QLatin1String(")")); + } + } else { + if (typeName.isEmpty()) + return; + changes.insert(m_file->startOf(ast), QLatin1String(" = new ") + typeName); + } + } + + void insertNewExpression(ChangeSet &changes) const + { + const QString typeName = typeNameOfDeclaration(); + if (typeName.isEmpty()) + return; + changes.insert(m_file->endOf(m_identifierAST->firstToken()), + QLatin1String(" = new ") + typeName); + } + + void convertToPointer(ChangeSet &changes) const + { + // Handle initializer. + if (m_declaratorAST->initializer) { + if (IdExpressionAST *idExprAST = m_declaratorAST->initializer->asIdExpression()) { + changes.insert(m_file->startOf(idExprAST), QLatin1String("&")); + } else if (CallAST *callAST = m_declaratorAST->initializer->asCall()) { + insertNewExpression(changes, callAST); + } else if (ExpressionListParenAST *exprListAST = m_declaratorAST->initializer + ->asExpressionListParen()) { + insertNewExpression(changes, exprListAST); + } else if (BracedInitializerAST *bracedInitializerAST = m_declaratorAST->initializer + ->asBracedInitializer()) { + insertNewExpression(changes, bracedInitializerAST); + } + } else { + insertNewExpression(changes); + } + + // Fix all occurrences of the identifier in this function. + ASTPath astPath(m_document); + const QList uses = semanticInfo().localUses.value(m_symbol); + for (const SemanticInfo::Use &use : uses) { + const QList path = astPath(use.line, use.column); + AST *idAST = path.last(); + bool insertStar = true; + for (int i = path.count() - 2; i >= 0; --i) { + if (m_isAutoDeclaration && path.at(i) == m_declaratorAST) { + insertStar = false; + break; + } + if (MemberAccessAST *memberAccessAST = path.at(i)->asMemberAccess()) { + const int pos = m_file->startOf(memberAccessAST->access_token); + changes.replace(pos, pos + 1, QLatin1String("->")); + insertStar = false; + break; + } else if (UnaryExpressionAST *unaryExprAST = path.at(i)->asUnaryExpression()) { + if (m_file->tokenAt(unaryExprAST->unary_op_token).kind() == T_AMPER) { + const int pos = m_file->startOf(unaryExprAST->unary_op_token); + changes.remove(pos, pos + 1); + insertStar = false; + break; + } + } else if (path.at(i)->asFunctionDefinition()) { + break; + } + } + if (insertStar) + changes.insert(m_file->startOf(idAST), QLatin1String("*")); + } + } + + const Mode m_mode; + const bool m_isAutoDeclaration; + const SimpleDeclarationAST * const m_simpleDeclaration; + const DeclaratorAST * const m_declaratorAST; + const SimpleNameAST * const m_identifierAST; + Symbol * const m_symbol; + const CppRefactoringChanges m_refactoring; + const CppRefactoringFilePtr m_file; + const Document::Ptr m_document; +}; + +/*! + Converts the selected variable to a pointer if it is a stack variable or reference, or vice versa. + Activates on variable declarations. + */ +class ConvertFromAndToPointer : public CppQuickFixFactory +{ +#ifdef WITH_TESTS +public: + static QObject *createTest(); +#endif + +private: + void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override + { + const QList &path = interface.path(); + if (path.count() < 2) + return; + SimpleNameAST *identifier = path.last()->asSimpleName(); + if (!identifier) + return; + SimpleDeclarationAST *simpleDeclaration = nullptr; + DeclaratorAST *declarator = nullptr; + bool isFunctionLocal = false; + bool isClassLocal = false; + ConvertFromAndToPointerOp::Mode mode = ConvertFromAndToPointerOp::FromVariable; + for (int i = path.count() - 2; i >= 0; --i) { + AST *ast = path.at(i); + if (!declarator && (declarator = ast->asDeclarator())) + continue; + if (!simpleDeclaration && (simpleDeclaration = ast->asSimpleDeclaration())) + continue; + if (declarator && simpleDeclaration) { + if (ast->asClassSpecifier()) { + isClassLocal = true; + } else if (ast->asFunctionDefinition() && !isClassLocal) { + isFunctionLocal = true; + break; + } + } + } + if (!isFunctionLocal || !simpleDeclaration || !declarator) + return; + + Symbol *symbol = nullptr; + for (List *lst = simpleDeclaration->symbols; lst; lst = lst->next) { + if (lst->value->name() == identifier->name) { + symbol = lst->value; + break; + } + } + if (!symbol) + return; + + bool isAutoDeclaration = false; + if (symbol->storage() == Symbol::Auto) { + // For auto variables we must deduce the type from the initializer. + if (!declarator->initializer) + return; + + isAutoDeclaration = true; + TypeOfExpression typeOfExpression; + typeOfExpression.init(interface.semanticInfo().doc, interface.snapshot()); + typeOfExpression.setExpandTemplates(true); + CppRefactoringFilePtr file = interface.currentFile(); + Scope *scope = file->scopeAt(declarator->firstToken()); + QList result = typeOfExpression(file->textOf(declarator->initializer).toUtf8(), + scope, TypeOfExpression::Preprocess); + if (!result.isEmpty() && result.first().type()->asPointerType()) + mode = ConvertFromAndToPointerOp::FromPointer; + } else if (declarator->ptr_operator_list) { + for (PtrOperatorListAST *ops = declarator->ptr_operator_list; ops; ops = ops->next) { + if (ops != declarator->ptr_operator_list) { + // Bail out on more complex pointer types (e.g. pointer of pointer, + // or reference of pointer). + return; + } + if (ops->value->asPointer()) + mode = ConvertFromAndToPointerOp::FromPointer; + else if (ops->value->asReference()) + mode = ConvertFromAndToPointerOp::FromReference; + } + } + + const int priority = path.size() - 1; + result << new ConvertFromAndToPointerOp(interface, priority, mode, isAutoDeclaration, + simpleDeclaration, declarator, identifier, symbol); + } +}; + +#ifdef WITH_TESTS +using namespace Tests; + +class ConvertFromAndToPointerTest : public QObject +{ + Q_OBJECT + +private slots: + void test_data() + { + QTest::addColumn("original"); + QTest::addColumn("expected"); + + QTest::newRow("ConvertFromPointer") + << QByteArray("void foo() {\n" + " QString *@str;\n" + " if (!str->isEmpty())\n" + " str->clear();\n" + " f1(*str);\n" + " f2(str);\n" + "}\n") + << QByteArray("void foo() {\n" + " QString str;\n" + " if (!str.isEmpty())\n" + " str.clear();\n" + " f1(str);\n" + " f2(&str);\n" + "}\n"); + + QTest::newRow("ConvertToPointer") + << QByteArray("void foo() {\n" + " QString @str;\n" + " if (!str.isEmpty())\n" + " str.clear();\n" + " f1(str);\n" + " f2(&str);\n" + "}\n") + << QByteArray("void foo() {\n" + " QString *str = new QString;\n" + " if (!str->isEmpty())\n" + " str->clear();\n" + " f1(*str);\n" + " f2(str);\n" + "}\n"); + + QTest::newRow("ConvertReferenceToPointer") + << QByteArray("void foo() {\n" + " QString narf;" + " QString &@str = narf;\n" + " if (!str.isEmpty())\n" + " str.clear();\n" + " f1(str);\n" + " f2(&str);\n" + "}\n") + << QByteArray("void foo() {\n" + " QString narf;" + " QString *str = &narf;\n" + " if (!str->isEmpty())\n" + " str->clear();\n" + " f1(*str);\n" + " f2(str);\n" + "}\n"); + + QTest::newRow("ConvertFromPointer_withInitializer") + << QByteArray("void foo() {\n" + " QString *@str = new QString(QLatin1String(\"schnurz\"));\n" + " if (!str->isEmpty())\n" + " str->clear();\n" + "}\n") + << QByteArray("void foo() {\n" + " QString str(QLatin1String(\"schnurz\"));\n" + " if (!str.isEmpty())\n" + " str.clear();\n" + "}\n"); + + QTest::newRow("ConvertFromPointer_withBareInitializer") + << QByteArray("void foo() {\n" + " QString *@str = new QString;\n" + " if (!str->isEmpty())\n" + " str->clear();\n" + "}\n") + << QByteArray("void foo() {\n" + " QString str;\n" + " if (!str.isEmpty())\n" + " str.clear();\n" + "}\n"); + + QTest::newRow("ConvertFromPointer_withEmptyInitializer") + << QByteArray("void foo() {\n" + " QString *@str = new QString();\n" + " if (!str->isEmpty())\n" + " str->clear();\n" + "}\n") + << QByteArray("void foo() {\n" + " QString str;\n" + " if (!str.isEmpty())\n" + " str.clear();\n" + "}\n"); + + QTest::newRow("ConvertFromPointer_structWithPointer") + << QByteArray("struct Bar{ QString *str; };\n" + "void foo() {\n" + " Bar *@bar = new Bar;\n" + " bar->str = new QString;\n" + " delete bar->str;\n" + " delete bar;\n" + "}\n") + << QByteArray("struct Bar{ QString *str; };\n" + "void foo() {\n" + " Bar bar;\n" + " bar.str = new QString;\n" + " delete bar.str;\n" + " // delete bar;\n" + "}\n"); + + QTest::newRow("ConvertToPointer_withInitializer") + << QByteArray("void foo() {\n" + " QString @str = QLatin1String(\"narf\");\n" + " if (!str.isEmpty())\n" + " str.clear();\n" + "}\n") + << QByteArray("void foo() {\n" + " QString *str = new QString(QLatin1String(\"narf\"));\n" + " if (!str->isEmpty())\n" + " str->clear();\n" + "}\n"); + + QTest::newRow("ConvertToPointer_withParenInitializer") + << QByteArray("void foo() {\n" + " QString @str(QLatin1String(\"narf\"));\n" + " if (!str.isEmpty())\n" + " str.clear();\n" + "}\n") + << QByteArray("void foo() {\n" + " QString *str = new QString(QLatin1String(\"narf\"));\n" + " if (!str->isEmpty())\n" + " str->clear();\n" + "}\n"); + + QTest::newRow("ConvertToPointer_noTriggerRValueRefs") + << QByteArray("void foo(Narf &&@narf) {}\n") + << QByteArray(); + + QTest::newRow("ConvertToPointer_noTriggerGlobal") + << QByteArray("int @global;\n") + << QByteArray(); + + QTest::newRow("ConvertToPointer_noTriggerClassMember") + << QByteArray("struct C { int @member; };\n") + << QByteArray(); + + QTest::newRow("ConvertToPointer_noTriggerClassMember2") + << QByteArray("void f() { struct C { int @member; }; }\n") + << QByteArray(); + + QTest::newRow("ConvertToPointer_functionOfFunctionLocalClass") + << QByteArray("void f() {\n" + " struct C {\n" + " void g() { int @member; }\n" + " };\n" + "}\n") + << QByteArray("void f() {\n" + " struct C {\n" + " void g() { int *member; }\n" + " };\n" + "}\n"); + + QTest::newRow("ConvertToPointer_redeclaredVariable_block") + << QByteArray("void foo() {\n" + " QString @str;\n" + " str.clear();\n" + " {\n" + " QString str;\n" + " str.clear();\n" + " }\n" + " f1(str);\n" + "}\n") + << QByteArray("void foo() {\n" + " QString *str = new QString;\n" + " str->clear();\n" + " {\n" + " QString str;\n" + " str.clear();\n" + " }\n" + " f1(*str);\n" + "}\n"); + + QTest::newRow("ConvertAutoFromPointer") + << QByteArray("void foo() {\n" + " auto @str = new QString(QLatin1String(\"foo\"));\n" + " if (!str->isEmpty())\n" + " str->clear();\n" + " f1(*str);\n" + " f2(str);\n" + "}\n") + << QByteArray("void foo() {\n" + " auto str = QString(QLatin1String(\"foo\"));\n" + " if (!str.isEmpty())\n" + " str.clear();\n" + " f1(str);\n" + " f2(&str);\n" + "}\n"); + + QTest::newRow("ConvertAutoFromPointer2") + << QByteArray("void foo() {\n" + " auto *@str = new QString;\n" + " if (!str->isEmpty())\n" + " str->clear();\n" + " f1(*str);\n" + " f2(str);\n" + "}\n") + << QByteArray("void foo() {\n" + " auto str = QString();\n" + " if (!str.isEmpty())\n" + " str.clear();\n" + " f1(str);\n" + " f2(&str);\n" + "}\n"); + + QTest::newRow("ConvertAutoToPointer") + << QByteArray("void foo() {\n" + " auto @str = QString(QLatin1String(\"foo\"));\n" + " if (!str.isEmpty())\n" + " str.clear();\n" + " f1(str);\n" + " f2(&str);\n" + "}\n") + << QByteArray("void foo() {\n" + " auto @str = new QString(QLatin1String(\"foo\"));\n" + " if (!str->isEmpty())\n" + " str->clear();\n" + " f1(*str);\n" + " f2(str);\n" + "}\n"); + + QTest::newRow("ConvertToPointerWithMacro") + << QByteArray("#define BAR bar\n" + "void func()\n" + "{\n" + " int @foo = 42;\n" + " int bar;\n" + " BAR = foo;\n" + "}\n") + << QByteArray("#define BAR bar\n" + "void func()\n" + "{\n" + " int *foo = 42;\n" + " int bar;\n" + " BAR = *foo;\n" + "}\n"); + + QString testObjAndFunc = "struct Object\n" + "{\n" + " Object(%1){}\n" + "};\n" + "void func()\n" + "{\n" + " %2\n" + "}\n"; + + QTest::newRow("ConvertToStack1_QTCREATORBUG23181") + << QByteArray(testObjAndFunc.arg("int").arg("Object *@obj = new Object(0);").toUtf8()) + << QByteArray(testObjAndFunc.arg("int").arg("Object obj(0);").toUtf8()); + + QTest::newRow("ConvertToStack2_QTCREATORBUG23181") + << QByteArray(testObjAndFunc.arg("int").arg("Object *@obj = new Object{0};").toUtf8()) + << QByteArray(testObjAndFunc.arg("int").arg("Object obj{0};").toUtf8()); + + QTest::newRow("ConvertToPointer1_QTCREATORBUG23181") + << QByteArray(testObjAndFunc.arg("").arg("Object @obj;").toUtf8()) + << QByteArray(testObjAndFunc.arg("").arg("Object *obj = new Object;").toUtf8()); + + QTest::newRow("ConvertToPointer2_QTCREATORBUG23181") + << QByteArray(testObjAndFunc.arg("").arg("Object @obj();").toUtf8()) + << QByteArray(testObjAndFunc.arg("").arg("Object *obj = new Object();").toUtf8()); + + QTest::newRow("ConvertToPointer3_QTCREATORBUG23181") + << QByteArray(testObjAndFunc.arg("").arg("Object @obj{};").toUtf8()) + << QByteArray(testObjAndFunc.arg("").arg("Object *obj = new Object{};").toUtf8()); + + QTest::newRow("ConvertToPointer4_QTCREATORBUG23181") + << QByteArray(testObjAndFunc.arg("int").arg("Object @obj(0);").toUtf8()) + << QByteArray(testObjAndFunc.arg("int").arg("Object *obj = new Object(0);").toUtf8()); + + + } + + void test() + { + QFETCH(QByteArray, original); + QFETCH(QByteArray, expected); + ConvertFromAndToPointer factory; + QuickFixOperationTest(singleDocument(original, expected), &factory); + } +}; + +QObject *ConvertFromAndToPointer::createTest() { return new ConvertFromAndToPointerTest; } + +#endif // WITH_TESTS +} // namespace + +void registerConvertFromAndToPointerQuickfix() +{ + CppQuickFixFactory::registerFactory(); +} + +} // namespace CppEditor::Internal + +#ifdef WITH_TESTS +#include +#endif diff --git a/src/plugins/cppeditor/quickfixes/convertfromandtopointer.h b/src/plugins/cppeditor/quickfixes/convertfromandtopointer.h new file mode 100644 index 00000000000..0deb6c0da93 --- /dev/null +++ b/src/plugins/cppeditor/quickfixes/convertfromandtopointer.h @@ -0,0 +1,8 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +namespace CppEditor::Internal { +void registerConvertFromAndToPointerQuickfix(); +} // namespace CppEditor::Internal diff --git a/src/plugins/cppeditor/quickfixes/cppquickfix_test.cpp b/src/plugins/cppeditor/quickfixes/cppquickfix_test.cpp index d3201f4f7c3..58875331a12 100644 --- a/src/plugins/cppeditor/quickfixes/cppquickfix_test.cpp +++ b/src/plugins/cppeditor/quickfixes/cppquickfix_test.cpp @@ -1039,302 +1039,6 @@ void QuickfixTest::testGeneric_data() "}" ) << _(); - QTest::newRow("ConvertFromPointer") - << CppQuickFixFactoryPtr(new ConvertFromAndToPointer) - << _("void foo() {\n" - " QString *@str;\n" - " if (!str->isEmpty())\n" - " str->clear();\n" - " f1(*str);\n" - " f2(str);\n" - "}\n") - << _("void foo() {\n" - " QString str;\n" - " if (!str.isEmpty())\n" - " str.clear();\n" - " f1(str);\n" - " f2(&str);\n" - "}\n"); - - QTest::newRow("ConvertToPointer") - << CppQuickFixFactoryPtr(new ConvertFromAndToPointer) - << _("void foo() {\n" - " QString @str;\n" - " if (!str.isEmpty())\n" - " str.clear();\n" - " f1(str);\n" - " f2(&str);\n" - "}\n") - << _("void foo() {\n" - " QString *str = new QString;\n" - " if (!str->isEmpty())\n" - " str->clear();\n" - " f1(*str);\n" - " f2(str);\n" - "}\n"); - - QTest::newRow("ConvertReferenceToPointer") - << CppQuickFixFactoryPtr(new ConvertFromAndToPointer) - << _("void foo() {\n" - " QString narf;" - " QString &@str = narf;\n" - " if (!str.isEmpty())\n" - " str.clear();\n" - " f1(str);\n" - " f2(&str);\n" - "}\n") - << _("void foo() {\n" - " QString narf;" - " QString *str = &narf;\n" - " if (!str->isEmpty())\n" - " str->clear();\n" - " f1(*str);\n" - " f2(str);\n" - "}\n"); - - QTest::newRow("ConvertFromPointer_withInitializer") - << CppQuickFixFactoryPtr(new ConvertFromAndToPointer) - << _("void foo() {\n" - " QString *@str = new QString(QLatin1String(\"schnurz\"));\n" - " if (!str->isEmpty())\n" - " str->clear();\n" - "}\n") - << _("void foo() {\n" - " QString str(QLatin1String(\"schnurz\"));\n" - " if (!str.isEmpty())\n" - " str.clear();\n" - "}\n"); - - QTest::newRow("ConvertFromPointer_withBareInitializer") - << CppQuickFixFactoryPtr(new ConvertFromAndToPointer) - << _("void foo() {\n" - " QString *@str = new QString;\n" - " if (!str->isEmpty())\n" - " str->clear();\n" - "}\n") - << _("void foo() {\n" - " QString str;\n" - " if (!str.isEmpty())\n" - " str.clear();\n" - "}\n"); - - QTest::newRow("ConvertFromPointer_withEmptyInitializer") - << CppQuickFixFactoryPtr(new ConvertFromAndToPointer) - << _("void foo() {\n" - " QString *@str = new QString();\n" - " if (!str->isEmpty())\n" - " str->clear();\n" - "}\n") - << _("void foo() {\n" - " QString str;\n" - " if (!str.isEmpty())\n" - " str.clear();\n" - "}\n"); - - QTest::newRow("ConvertFromPointer_structWithPointer") - << CppQuickFixFactoryPtr(new ConvertFromAndToPointer) - << _("struct Bar{ QString *str; };\n" - "void foo() {\n" - " Bar *@bar = new Bar;\n" - " bar->str = new QString;\n" - " delete bar->str;\n" - " delete bar;\n" - "}\n") - << _("struct Bar{ QString *str; };\n" - "void foo() {\n" - " Bar bar;\n" - " bar.str = new QString;\n" - " delete bar.str;\n" - " // delete bar;\n" - "}\n"); - - QTest::newRow("ConvertToPointer_withInitializer") - << CppQuickFixFactoryPtr(new ConvertFromAndToPointer) - << _("void foo() {\n" - " QString @str = QLatin1String(\"narf\");\n" - " if (!str.isEmpty())\n" - " str.clear();\n" - "}\n") - << _("void foo() {\n" - " QString *str = new QString(QLatin1String(\"narf\"));\n" - " if (!str->isEmpty())\n" - " str->clear();\n" - "}\n"); - - QTest::newRow("ConvertToPointer_withParenInitializer") - << CppQuickFixFactoryPtr(new ConvertFromAndToPointer) - << _("void foo() {\n" - " QString @str(QLatin1String(\"narf\"));\n" - " if (!str.isEmpty())\n" - " str.clear();\n" - "}\n") - << _("void foo() {\n" - " QString *str = new QString(QLatin1String(\"narf\"));\n" - " if (!str->isEmpty())\n" - " str->clear();\n" - "}\n"); - - QTest::newRow("ConvertToPointer_noTriggerRValueRefs") - << CppQuickFixFactoryPtr(new ConvertFromAndToPointer) - << _("void foo(Narf &&@narf) {}\n") - << _(); - - QTest::newRow("ConvertToPointer_noTriggerGlobal") - << CppQuickFixFactoryPtr(new ConvertFromAndToPointer) - << _("int @global;\n") - << _(); - - QTest::newRow("ConvertToPointer_noTriggerClassMember") - << CppQuickFixFactoryPtr(new ConvertFromAndToPointer) - << _("struct C { int @member; };\n") - << _(); - - QTest::newRow("ConvertToPointer_noTriggerClassMember2") - << CppQuickFixFactoryPtr(new ConvertFromAndToPointer) - << _("void f() { struct C { int @member; }; }\n") - << _(); - - QTest::newRow("ConvertToPointer_functionOfFunctionLocalClass") - << CppQuickFixFactoryPtr(new ConvertFromAndToPointer) - << _("void f() {\n" - " struct C {\n" - " void g() { int @member; }\n" - " };\n" - "}\n") - << _("void f() {\n" - " struct C {\n" - " void g() { int *member; }\n" - " };\n" - "}\n"); - - QTest::newRow("ConvertToPointer_redeclaredVariable_block") - << CppQuickFixFactoryPtr(new ConvertFromAndToPointer) - << _("void foo() {\n" - " QString @str;\n" - " str.clear();\n" - " {\n" - " QString str;\n" - " str.clear();\n" - " }\n" - " f1(str);\n" - "}\n") - << _("void foo() {\n" - " QString *str = new QString;\n" - " str->clear();\n" - " {\n" - " QString str;\n" - " str.clear();\n" - " }\n" - " f1(*str);\n" - "}\n"); - - QTest::newRow("ConvertAutoFromPointer") - << CppQuickFixFactoryPtr(new ConvertFromAndToPointer) - << _("void foo() {\n" - " auto @str = new QString(QLatin1String(\"foo\"));\n" - " if (!str->isEmpty())\n" - " str->clear();\n" - " f1(*str);\n" - " f2(str);\n" - "}\n") - << _("void foo() {\n" - " auto str = QString(QLatin1String(\"foo\"));\n" - " if (!str.isEmpty())\n" - " str.clear();\n" - " f1(str);\n" - " f2(&str);\n" - "}\n"); - - QTest::newRow("ConvertAutoFromPointer2") - << CppQuickFixFactoryPtr(new ConvertFromAndToPointer) - << _("void foo() {\n" - " auto *@str = new QString;\n" - " if (!str->isEmpty())\n" - " str->clear();\n" - " f1(*str);\n" - " f2(str);\n" - "}\n") - << _("void foo() {\n" - " auto str = QString();\n" - " if (!str.isEmpty())\n" - " str.clear();\n" - " f1(str);\n" - " f2(&str);\n" - "}\n"); - - QTest::newRow("ConvertAutoToPointer") - << CppQuickFixFactoryPtr(new ConvertFromAndToPointer) - << _("void foo() {\n" - " auto @str = QString(QLatin1String(\"foo\"));\n" - " if (!str.isEmpty())\n" - " str.clear();\n" - " f1(str);\n" - " f2(&str);\n" - "}\n") - << _("void foo() {\n" - " auto @str = new QString(QLatin1String(\"foo\"));\n" - " if (!str->isEmpty())\n" - " str->clear();\n" - " f1(*str);\n" - " f2(str);\n" - "}\n"); - - QTest::newRow("ConvertToPointerWithMacro") - << CppQuickFixFactoryPtr(new ConvertFromAndToPointer) - << _("#define BAR bar\n" - "void func()\n" - "{\n" - " int @foo = 42;\n" - " int bar;\n" - " BAR = foo;\n" - "}\n") - << _("#define BAR bar\n" - "void func()\n" - "{\n" - " int *foo = 42;\n" - " int bar;\n" - " BAR = *foo;\n" - "}\n"); - - QString testObjAndFunc = "struct Object\n" - "{\n" - " Object(%1){}\n" - "};\n" - "void func()\n" - "{\n" - " %2\n" - "}\n"; - - QTest::newRow("ConvertToStack1_QTCREATORBUG23181") - << CppQuickFixFactoryPtr(new ConvertFromAndToPointer) - << _(testObjAndFunc.arg("int").arg("Object *@obj = new Object(0);").toUtf8()) - << _(testObjAndFunc.arg("int").arg("Object obj(0);").toUtf8()); - - QTest::newRow("ConvertToStack2_QTCREATORBUG23181") - << CppQuickFixFactoryPtr(new ConvertFromAndToPointer) - << _(testObjAndFunc.arg("int").arg("Object *@obj = new Object{0};").toUtf8()) - << _(testObjAndFunc.arg("int").arg("Object obj{0};").toUtf8()); - - QTest::newRow("ConvertToPointer1_QTCREATORBUG23181") - << CppQuickFixFactoryPtr(new ConvertFromAndToPointer) - << _(testObjAndFunc.arg("").arg("Object @obj;").toUtf8()) - << _(testObjAndFunc.arg("").arg("Object *obj = new Object;").toUtf8()); - - QTest::newRow("ConvertToPointer2_QTCREATORBUG23181") - << CppQuickFixFactoryPtr(new ConvertFromAndToPointer) - << _(testObjAndFunc.arg("").arg("Object @obj();").toUtf8()) - << _(testObjAndFunc.arg("").arg("Object *obj = new Object();").toUtf8()); - - QTest::newRow("ConvertToPointer3_QTCREATORBUG23181") - << CppQuickFixFactoryPtr(new ConvertFromAndToPointer) - << _(testObjAndFunc.arg("").arg("Object @obj{};").toUtf8()) - << _(testObjAndFunc.arg("").arg("Object *obj = new Object{};").toUtf8()); - - QTest::newRow("ConvertToPointer4_QTCREATORBUG23181") - << CppQuickFixFactoryPtr(new ConvertFromAndToPointer) - << _(testObjAndFunc.arg("int").arg("Object @obj(0);").toUtf8()) - << _(testObjAndFunc.arg("int").arg("Object *obj = new Object(0);").toUtf8()); - QTest::newRow("convert to camel case: normal") << CppQuickFixFactoryPtr(new ConvertToCamelCase(true)) << _("void @lower_case_function();\n") diff --git a/src/plugins/cppeditor/quickfixes/cppquickfixes.cpp b/src/plugins/cppeditor/quickfixes/cppquickfixes.cpp index f40907c2928..73a994f2a80 100644 --- a/src/plugins/cppeditor/quickfixes/cppquickfixes.cpp +++ b/src/plugins/cppeditor/quickfixes/cppquickfixes.cpp @@ -14,6 +14,7 @@ #include "../insertionpointlocator.h" #include "../symbolfinder.h" #include "bringidentifierintoscope.h" +#include "convertfromandtopointer.h" #include "cppcodegenerationquickfixes.h" #include "cppinsertvirtualmethods.h" #include "cppquickfixassistant.h" @@ -89,9 +90,7 @@ #include #include -#include #include -#include using namespace CPlusPlus; using namespace ProjectExplorer; @@ -870,368 +869,6 @@ void CompleteSwitchCaseStatement::doMatch(const CppQuickFixInterface &interface, namespace { -class ConvertFromAndToPointerOp : public CppQuickFixOperation -{ -public: - enum Mode { FromPointer, FromVariable, FromReference }; - - ConvertFromAndToPointerOp(const CppQuickFixInterface &interface, int priority, Mode mode, - bool isAutoDeclaration, - const SimpleDeclarationAST *simpleDeclaration, - const DeclaratorAST *declaratorAST, - const SimpleNameAST *identifierAST, - Symbol *symbol) - : CppQuickFixOperation(interface, priority) - , m_mode(mode) - , m_isAutoDeclaration(isAutoDeclaration) - , m_simpleDeclaration(simpleDeclaration) - , m_declaratorAST(declaratorAST) - , m_identifierAST(identifierAST) - , m_symbol(symbol) - , m_refactoring(snapshot()) - , m_file(m_refactoring.cppFile(filePath())) - , m_document(interface.semanticInfo().doc) - { - setDescription( - mode == FromPointer - ? Tr::tr("Convert to Stack Variable") - : Tr::tr("Convert to Pointer")); - } - - void perform() override - { - ChangeSet changes; - - switch (m_mode) { - case FromPointer: - removePointerOperator(changes); - convertToStackVariable(changes); - break; - case FromReference: - removeReferenceOperator(changes); - Q_FALLTHROUGH(); - case FromVariable: - convertToPointer(changes); - break; - } - - m_file->setChangeSet(changes); - m_file->apply(); - } - -private: - void removePointerOperator(ChangeSet &changes) const - { - if (!m_declaratorAST->ptr_operator_list) - return; - PointerAST *ptrAST = m_declaratorAST->ptr_operator_list->value->asPointer(); - QTC_ASSERT(ptrAST, return); - const int pos = m_file->startOf(ptrAST->star_token); - changes.remove(pos, pos + 1); - } - - void removeReferenceOperator(ChangeSet &changes) const - { - ReferenceAST *refAST = m_declaratorAST->ptr_operator_list->value->asReference(); - QTC_ASSERT(refAST, return); - const int pos = m_file->startOf(refAST->reference_token); - changes.remove(pos, pos + 1); - } - - void removeNewExpression(ChangeSet &changes, NewExpressionAST *newExprAST) const - { - ExpressionListAST *exprlist = nullptr; - if (newExprAST->new_initializer) { - if (ExpressionListParenAST *ast = newExprAST->new_initializer->asExpressionListParen()) - exprlist = ast->expression_list; - else if (BracedInitializerAST *ast = newExprAST->new_initializer->asBracedInitializer()) - exprlist = ast->expression_list; - } - - if (exprlist) { - // remove 'new' keyword and type before initializer - changes.remove(m_file->startOf(newExprAST->new_token), - m_file->startOf(newExprAST->new_initializer)); - - changes.remove(m_file->endOf(m_declaratorAST->equal_token - 1), - m_file->startOf(m_declaratorAST->equal_token + 1)); - } else { - // remove the whole new expression - changes.remove(m_file->endOf(m_identifierAST->firstToken()), - m_file->startOf(newExprAST->lastToken())); - } - } - - void removeNewKeyword(ChangeSet &changes, NewExpressionAST *newExprAST) const - { - // remove 'new' keyword before initializer - changes.remove(m_file->startOf(newExprAST->new_token), - m_file->startOf(newExprAST->new_type_id)); - } - - void convertToStackVariable(ChangeSet &changes) const - { - // Handle the initializer. - if (m_declaratorAST->initializer) { - if (NewExpressionAST *newExpression = m_declaratorAST->initializer->asNewExpression()) { - if (m_isAutoDeclaration) { - if (!newExpression->new_initializer) - changes.insert(m_file->endOf(newExpression), QStringLiteral("()")); - removeNewKeyword(changes, newExpression); - } else { - removeNewExpression(changes, newExpression); - } - } - } - - // Fix all occurrences of the identifier in this function. - ASTPath astPath(m_document); - const QList uses = semanticInfo().localUses.value(m_symbol); - for (const SemanticInfo::Use &use : uses) { - const QList path = astPath(use.line, use.column); - AST *idAST = path.last(); - bool declarationFound = false; - bool starFound = false; - int ampersandPos = 0; - bool memberAccess = false; - bool deleteCall = false; - - for (int i = path.count() - 2; i >= 0; --i) { - if (path.at(i) == m_declaratorAST) { - declarationFound = true; - break; - } - if (MemberAccessAST *memberAccessAST = path.at(i)->asMemberAccess()) { - if (m_file->tokenAt(memberAccessAST->access_token).kind() != T_ARROW) - continue; - int pos = m_file->startOf(memberAccessAST->access_token); - changes.replace(pos, pos + 2, QLatin1String(".")); - memberAccess = true; - break; - } else if (DeleteExpressionAST *deleteAST = path.at(i)->asDeleteExpression()) { - const int pos = m_file->startOf(deleteAST->delete_token); - changes.insert(pos, QLatin1String("// ")); - deleteCall = true; - break; - } else if (UnaryExpressionAST *unaryExprAST = path.at(i)->asUnaryExpression()) { - const Token tk = m_file->tokenAt(unaryExprAST->unary_op_token); - if (tk.kind() == T_STAR) { - if (!starFound) { - int pos = m_file->startOf(unaryExprAST->unary_op_token); - changes.remove(pos, pos + 1); - } - starFound = true; - } else if (tk.kind() == T_AMPER) { - ampersandPos = m_file->startOf(unaryExprAST->unary_op_token); - } - } else if (PointerAST *ptrAST = path.at(i)->asPointer()) { - if (!starFound) { - const int pos = m_file->startOf(ptrAST->star_token); - changes.remove(pos, pos); - } - starFound = true; - } else if (path.at(i)->asFunctionDefinition()) { - break; - } - } - if (!declarationFound && !starFound && !memberAccess && !deleteCall) { - if (ampersandPos) { - changes.insert(ampersandPos, QLatin1String("&(")); - changes.insert(m_file->endOf(idAST->firstToken()), QLatin1String(")")); - } else { - changes.insert(m_file->startOf(idAST), QLatin1String("&")); - } - } - } - } - - QString typeNameOfDeclaration() const - { - if (!m_simpleDeclaration - || !m_simpleDeclaration->decl_specifier_list - || !m_simpleDeclaration->decl_specifier_list->value) { - return QString(); - } - NamedTypeSpecifierAST *namedType - = m_simpleDeclaration->decl_specifier_list->value->asNamedTypeSpecifier(); - if (!namedType) - return QString(); - - Overview overview; - return overview.prettyName(namedType->name->name); - } - - void insertNewExpression(ChangeSet &changes, ExpressionAST *ast) const - { - const QString typeName = typeNameOfDeclaration(); - if (CallAST *callAST = ast->asCall()) { - if (typeName.isEmpty()) { - changes.insert(m_file->startOf(callAST), QLatin1String("new ")); - } else { - changes.insert(m_file->startOf(callAST), - QLatin1String("new ") + typeName + QLatin1Char('(')); - changes.insert(m_file->startOf(callAST->lastToken()), QLatin1String(")")); - } - } else { - if (typeName.isEmpty()) - return; - changes.insert(m_file->startOf(ast), QLatin1String(" = new ") + typeName); - } - } - - void insertNewExpression(ChangeSet &changes) const - { - const QString typeName = typeNameOfDeclaration(); - if (typeName.isEmpty()) - return; - changes.insert(m_file->endOf(m_identifierAST->firstToken()), - QLatin1String(" = new ") + typeName); - } - - void convertToPointer(ChangeSet &changes) const - { - // Handle initializer. - if (m_declaratorAST->initializer) { - if (IdExpressionAST *idExprAST = m_declaratorAST->initializer->asIdExpression()) { - changes.insert(m_file->startOf(idExprAST), QLatin1String("&")); - } else if (CallAST *callAST = m_declaratorAST->initializer->asCall()) { - insertNewExpression(changes, callAST); - } else if (ExpressionListParenAST *exprListAST = m_declaratorAST->initializer - ->asExpressionListParen()) { - insertNewExpression(changes, exprListAST); - } else if (BracedInitializerAST *bracedInitializerAST = m_declaratorAST->initializer - ->asBracedInitializer()) { - insertNewExpression(changes, bracedInitializerAST); - } - } else { - insertNewExpression(changes); - } - - // Fix all occurrences of the identifier in this function. - ASTPath astPath(m_document); - const QList uses = semanticInfo().localUses.value(m_symbol); - for (const SemanticInfo::Use &use : uses) { - const QList path = astPath(use.line, use.column); - AST *idAST = path.last(); - bool insertStar = true; - for (int i = path.count() - 2; i >= 0; --i) { - if (m_isAutoDeclaration && path.at(i) == m_declaratorAST) { - insertStar = false; - break; - } - if (MemberAccessAST *memberAccessAST = path.at(i)->asMemberAccess()) { - const int pos = m_file->startOf(memberAccessAST->access_token); - changes.replace(pos, pos + 1, QLatin1String("->")); - insertStar = false; - break; - } else if (UnaryExpressionAST *unaryExprAST = path.at(i)->asUnaryExpression()) { - if (m_file->tokenAt(unaryExprAST->unary_op_token).kind() == T_AMPER) { - const int pos = m_file->startOf(unaryExprAST->unary_op_token); - changes.remove(pos, pos + 1); - insertStar = false; - break; - } - } else if (path.at(i)->asFunctionDefinition()) { - break; - } - } - if (insertStar) - changes.insert(m_file->startOf(idAST), QLatin1String("*")); - } - } - - const Mode m_mode; - const bool m_isAutoDeclaration; - const SimpleDeclarationAST * const m_simpleDeclaration; - const DeclaratorAST * const m_declaratorAST; - const SimpleNameAST * const m_identifierAST; - Symbol * const m_symbol; - const CppRefactoringChanges m_refactoring; - const CppRefactoringFilePtr m_file; - const Document::Ptr m_document; -}; - -} // anonymous namespace - -void ConvertFromAndToPointer::doMatch(const CppQuickFixInterface &interface, - QuickFixOperations &result) -{ - const QList &path = interface.path(); - if (path.count() < 2) - return; - SimpleNameAST *identifier = path.last()->asSimpleName(); - if (!identifier) - return; - SimpleDeclarationAST *simpleDeclaration = nullptr; - DeclaratorAST *declarator = nullptr; - bool isFunctionLocal = false; - bool isClassLocal = false; - ConvertFromAndToPointerOp::Mode mode = ConvertFromAndToPointerOp::FromVariable; - for (int i = path.count() - 2; i >= 0; --i) { - AST *ast = path.at(i); - if (!declarator && (declarator = ast->asDeclarator())) - continue; - if (!simpleDeclaration && (simpleDeclaration = ast->asSimpleDeclaration())) - continue; - if (declarator && simpleDeclaration) { - if (ast->asClassSpecifier()) { - isClassLocal = true; - } else if (ast->asFunctionDefinition() && !isClassLocal) { - isFunctionLocal = true; - break; - } - } - } - if (!isFunctionLocal || !simpleDeclaration || !declarator) - return; - - Symbol *symbol = nullptr; - for (List *lst = simpleDeclaration->symbols; lst; lst = lst->next) { - if (lst->value->name() == identifier->name) { - symbol = lst->value; - break; - } - } - if (!symbol) - return; - - bool isAutoDeclaration = false; - if (symbol->storage() == Symbol::Auto) { - // For auto variables we must deduce the type from the initializer. - if (!declarator->initializer) - return; - - isAutoDeclaration = true; - TypeOfExpression typeOfExpression; - typeOfExpression.init(interface.semanticInfo().doc, interface.snapshot()); - typeOfExpression.setExpandTemplates(true); - CppRefactoringFilePtr file = interface.currentFile(); - Scope *scope = file->scopeAt(declarator->firstToken()); - QList result = typeOfExpression(file->textOf(declarator->initializer).toUtf8(), - scope, TypeOfExpression::Preprocess); - if (!result.isEmpty() && result.first().type()->asPointerType()) - mode = ConvertFromAndToPointerOp::FromPointer; - } else if (declarator->ptr_operator_list) { - for (PtrOperatorListAST *ops = declarator->ptr_operator_list; ops; ops = ops->next) { - if (ops != declarator->ptr_operator_list) { - // Bail out on more complex pointer types (e.g. pointer of pointer, - // or reference of pointer). - return; - } - if (ops->value->asPointer()) - mode = ConvertFromAndToPointerOp::FromPointer; - else if (ops->value->asReference()) - mode = ConvertFromAndToPointerOp::FromReference; - } - } - - const int priority = path.size() - 1; - result << new ConvertFromAndToPointerOp(interface, priority, mode, isAutoDeclaration, - simpleDeclaration, declarator, identifier, symbol); -} - -namespace { - class ApplyDeclDefLinkOperation : public CppQuickFixOperation { public: @@ -1645,7 +1282,6 @@ void createCppQuickFixes() new CompleteSwitchCaseStatement; new ApplyDeclDefLinkChanges; - new ConvertFromAndToPointer; new AssignToLocalVariable; @@ -1664,6 +1300,7 @@ void createCppQuickFixes() registerRewriteCommentQuickfixes(); registerExtractFunctionQuickfix(); registerExtractLiteralAsParameterQuickfix(); + registerConvertFromAndToPointerQuickfix(); new ExtraRefactoringOperations; diff --git a/src/plugins/cppeditor/quickfixes/cppquickfixes.h b/src/plugins/cppeditor/quickfixes/cppquickfixes.h index 444d7c99a92..487816132dd 100644 --- a/src/plugins/cppeditor/quickfixes/cppquickfixes.h +++ b/src/plugins/cppeditor/quickfixes/cppquickfixes.h @@ -125,17 +125,6 @@ private: void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override; }; -/*! - Converts the selected variable to a pointer if it is a stack variable or reference, or vice versa. - - Activates on variable declarations. - */ -class ConvertFromAndToPointer : public CppQuickFixFactory -{ -public: - void doMatch(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override; -}; - /*! Applies function signature changes */