From 4cd59fc357e01a792259845253dbbd9a88b868ad Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Thu, 16 May 2024 13:09:31 +0200 Subject: [PATCH] CppEditor: Move control statement quickfixes into dedicated files Change-Id: Ie4fb4bb466c151cc7666aecb5307fee6f6fd56d8 Reviewed-by: Christian Stenger Reviewed-by: --- src/plugins/cppeditor/CMakeLists.txt | 1 + src/plugins/cppeditor/cppeditor.qbs | 2 + .../cppeditor/quickfixes/cppquickfix_test.cpp | 551 ------- .../cppeditor/quickfixes/cppquickfix_test.h | 3 - .../cppeditor/quickfixes/cppquickfixes.cpp | 614 +------- .../cppeditor/quickfixes/cppquickfixes.h | 90 -- .../quickfixes/rewritecontrolstatements.cpp | 1342 +++++++++++++++++ .../quickfixes/rewritecontrolstatements.h | 8 + 8 files changed, 1355 insertions(+), 1256 deletions(-) create mode 100644 src/plugins/cppeditor/quickfixes/rewritecontrolstatements.cpp create mode 100644 src/plugins/cppeditor/quickfixes/rewritecontrolstatements.h diff --git a/src/plugins/cppeditor/CMakeLists.txt b/src/plugins/cppeditor/CMakeLists.txt index d8e9f9946cd..16beeb64fa0 100644 --- a/src/plugins/cppeditor/CMakeLists.txt +++ b/src/plugins/cppeditor/CMakeLists.txt @@ -115,6 +115,7 @@ add_qtc_plugin(CppEditor quickfixes/moveclasstoownfile.cpp quickfixes/moveclasstoownfile.h quickfixes/movefunctiondefinition.cpp quickfixes/movefunctiondefinition.h quickfixes/removeusingnamespace.cpp quickfixes/removeusingnamespace.h + quickfixes/rewritecontrolstatements.cpp quickfixes/rewritecontrolstatements.h resourcepreviewhoverhandler.cpp resourcepreviewhoverhandler.h searchsymbols.cpp searchsymbols.h semantichighlighter.cpp semantichighlighter.h diff --git a/src/plugins/cppeditor/cppeditor.qbs b/src/plugins/cppeditor/cppeditor.qbs index 864315f0b46..74f9ba1e5aa 100644 --- a/src/plugins/cppeditor/cppeditor.qbs +++ b/src/plugins/cppeditor/cppeditor.qbs @@ -259,6 +259,8 @@ QtcPlugin { "movefunctiondefinition.h", "removeusingnamespace.cpp", "removeusingnamespace.h", + "rewritecontrolstatements.cpp", + "rewritecontrolstatements.h", ] } diff --git a/src/plugins/cppeditor/quickfixes/cppquickfix_test.cpp b/src/plugins/cppeditor/quickfixes/cppquickfix_test.cpp index 05da9a46e36..67ffbcecc7e 100644 --- a/src/plugins/cppeditor/quickfixes/cppquickfix_test.cpp +++ b/src/plugins/cppeditor/quickfixes/cppquickfix_test.cpp @@ -841,110 +841,6 @@ void QuickfixTest::testGeneric_data() "};\n" ); - QTest::newRow("MoveDeclarationOutOfIf_ifOnly") - << CppQuickFixFactoryPtr(new MoveDeclarationOutOfIf) << _( - "void f()\n" - "{\n" - " if (Foo *@foo = g())\n" - " h();\n" - "}\n" - ) << _( - "void f()\n" - "{\n" - " Foo *foo = g();\n" - " if (foo)\n" - " h();\n" - "}\n" - ); - - QTest::newRow("MoveDeclarationOutOfIf_ifElse") - << CppQuickFixFactoryPtr(new MoveDeclarationOutOfIf) << _( - "void f()\n" - "{\n" - " if (Foo *@foo = g())\n" - " h();\n" - " else\n" - " i();\n" - "}\n" - ) << _( - "void f()\n" - "{\n" - " Foo *foo = g();\n" - " if (foo)\n" - " h();\n" - " else\n" - " i();\n" - "}\n" - ); - - QTest::newRow("MoveDeclarationOutOfIf_ifElseIf") - << CppQuickFixFactoryPtr(new MoveDeclarationOutOfIf) << _( - "void f()\n" - "{\n" - " if (Foo *foo = g()) {\n" - " if (Bar *@bar = x()) {\n" - " h();\n" - " j();\n" - " }\n" - " } else {\n" - " i();\n" - " }\n" - "}\n" - ) << _( - "void f()\n" - "{\n" - " if (Foo *foo = g()) {\n" - " Bar *bar = x();\n" - " if (bar) {\n" - " h();\n" - " j();\n" - " }\n" - " } else {\n" - " i();\n" - " }\n" - "}\n" - ); - - QTest::newRow("MoveDeclarationOutOfWhile_singleWhile") - << CppQuickFixFactoryPtr(new MoveDeclarationOutOfWhile) << _( - "void f()\n" - "{\n" - " while (Foo *@foo = g())\n" - " j();\n" - "}\n" - ) << _( - "void f()\n" - "{\n" - " Foo *foo;\n" - " while ((foo = g()) != 0)\n" - " j();\n" - "}\n" - ); - - QTest::newRow("MoveDeclarationOutOfWhile_whileInWhile") - << CppQuickFixFactoryPtr(new MoveDeclarationOutOfWhile) << _( - "void f()\n" - "{\n" - " while (Foo *foo = g()) {\n" - " while (Bar *@bar = h()) {\n" - " i();\n" - " j();\n" - " }\n" - " }\n" - "}\n" - ) << _( - "void f()\n" - "{\n" - " while (Foo *foo = g()) {\n" - " Bar *bar;\n" - " while ((bar = h()) != 0) {\n" - " i();\n" - " j();\n" - " }\n" - " }\n" - "}\n" - ); - // Check: Just a basic test since the main functionality is tested in // cpppointerdeclarationformatter_test.cpp QTest::newRow("ReformatPointerDeclaration") @@ -1184,60 +1080,6 @@ void QuickfixTest::testGeneric_data() "};\n" ); - // Check: optimize postcrement - QTest::newRow("OptimizeForLoop_postcrement") - << CppQuickFixFactoryPtr(new OptimizeForLoop) - << _("void foo() {f@or (int i = 0; i < 3; i++) {}}\n") - << _("void foo() {for (int i = 0; i < 3; ++i) {}}\n"); - - // Check: optimize condition - QTest::newRow("OptimizeForLoop_condition") - << CppQuickFixFactoryPtr(new OptimizeForLoop) - << _("void foo() {f@or (int i = 0; i < 3 + 5; ++i) {}}\n") - << _("void foo() {for (int i = 0, total = 3 + 5; i < total; ++i) {}}\n"); - - // Check: optimize fliped condition - QTest::newRow("OptimizeForLoop_flipedCondition") - << CppQuickFixFactoryPtr(new OptimizeForLoop) - << _("void foo() {f@or (int i = 0; 3 + 5 > i; ++i) {}}\n") - << _("void foo() {for (int i = 0, total = 3 + 5; total > i; ++i) {}}\n"); - - // Check: if "total" used, create other name. - QTest::newRow("OptimizeForLoop_alterVariableName") - << CppQuickFixFactoryPtr(new OptimizeForLoop) - << _("void foo() {f@or (int i = 0, total = 0; i < 3 + 5; ++i) {}}\n") - << _("void foo() {for (int i = 0, total = 0, totalX = 3 + 5; i < totalX; ++i) {}}\n"); - - // Check: optimize postcrement and condition - QTest::newRow("OptimizeForLoop_optimizeBoth") - << CppQuickFixFactoryPtr(new OptimizeForLoop) - << _("void foo() {f@or (int i = 0; i < 3 + 5; i++) {}}\n") - << _("void foo() {for (int i = 0, total = 3 + 5; i < total; ++i) {}}\n"); - - // Check: empty initializier - QTest::newRow("OptimizeForLoop_emptyInitializer") - << CppQuickFixFactoryPtr(new OptimizeForLoop) - << _("int i; void foo() {f@or (; i < 3 + 5; ++i) {}}\n") - << _("int i; void foo() {for (int total = 3 + 5; i < total; ++i) {}}\n"); - - // Check: wrong initializier type -> no trigger - QTest::newRow("OptimizeForLoop_wrongInitializer") - << CppQuickFixFactoryPtr(new OptimizeForLoop) - << _("int i; void foo() {f@or (double a = 0; i < 3 + 5; ++i) {}}\n") - << _(); - - // Check: No trigger when numeric - QTest::newRow("OptimizeForLoop_noTriggerNumeric1") - << CppQuickFixFactoryPtr(new OptimizeForLoop) - << _("void foo() {fo@r (int i = 0; i < 3; ++i) {}}\n") - << _(); - - // Check: No trigger when numeric - QTest::newRow("OptimizeForLoop_noTriggerNumeric2") - << CppQuickFixFactoryPtr(new OptimizeForLoop) - << _("void foo() {fo@r (int i = 0; i < -3; ++i) {}}\n") - << _(); - QTest::newRow("ConvertFromPointer") << CppQuickFixFactoryPtr(new ConvertFromAndToPointer) << _("void foo() {\n" @@ -1899,399 +1741,6 @@ void QuickfixTest::testExtractLiteralAsParameterNotTriggeringForInvalidCode() QuickFixOperationTest(testDocuments, &factory); } -void QuickfixTest::testAddCurlyBraces_data() -{ - QTest::addColumn("original"); - QTest::addColumn("expected"); - - QByteArray original = R"delim( -void MyObject::f() -{ - @if (true) - emit mySig(); -})delim"; - QByteArray expected = R"delim( -void MyObject::f() -{ - if (true) { - emit mySig(); - } -})delim"; - QTest::newRow("if") << original << expected; - - original = R"delim( -void MyObject::f() -{ - @if (true) - emit mySig(); - else - emit otherSig(); -})delim"; - expected = R"delim( -void MyObject::f() -{ - @if (true) { - emit mySig(); - } else { - emit otherSig(); - } -})delim"; - QTest::newRow("if with one else, unbraced") << original << expected; - - original = R"delim( -void MyObject::f() -{ - @if (true) { - emit mySig(); - } else - emit otherSig(); -})delim"; - expected = R"delim( -void MyObject::f() -{ - @if (true) { - emit mySig(); - } else { - emit otherSig(); - } -})delim"; - QTest::newRow("if with one else, if braced") << original << expected; - - original = R"delim( -void MyObject::f() -{ - @if (true) - emit mySig(); - else { - emit otherSig(); - } -})delim"; - expected = R"delim( -void MyObject::f() -{ - @if (true) { - emit mySig(); - } else { - emit otherSig(); - } -})delim"; - QTest::newRow("if with one else, else braced") << original << expected; - - original = R"delim( -void MyObject::f() -{ - @if (true) { - emit mySig(); - } else { - emit otherSig(); - } -})delim"; - expected.clear(); - QTest::newRow("if with one else, both braced") << original << expected; - - original = R"delim( -void MyObject::f() -{ - @if (x == 1) - emit sig1(); - else if (x == 2) - emit sig2(); -})delim"; - expected = R"delim( -void MyObject::f() -{ - if (x == 1) { - emit sig1(); - } else if (x == 2) { - emit sig2(); - } -})delim"; - QTest::newRow("if-else chain without final else, unbraced") << original << expected; - - original = R"delim( -void MyObject::f() -{ - @if (x == 1) { - emit sig1(); - } else if (x == 2) - emit sig2(); -})delim"; - expected = R"delim( -void MyObject::f() -{ - if (x == 1) { - emit sig1(); - } else if (x == 2) { - emit sig2(); - } -})delim"; - QTest::newRow("if-else chain without final else, partially braced 1") << original << expected; - - original = R"delim( -void MyObject::f() -{ - @if (x == 1) - emit sig1(); - else if (x == 2) { - emit sig2(); - } -})delim"; - expected = R"delim( -void MyObject::f() -{ - if (x == 1) { - emit sig1(); - } else if (x == 2) { - emit sig2(); - } -})delim"; - QTest::newRow("if-else chain without final else, partially braced 2") << original << expected; - - original = R"delim( -void MyObject::f() -{ - @if (x == 1) { - emit sig1(); - } else if (x == 2) { - emit sig2(); - } -})delim"; - expected.clear(); - QTest::newRow("if-else chain without final else, fully braced") << original << expected; - - original = R"delim( -void MyObject::f() -{ - @if (x == 1) - emit sig1(); - else if (x == 2) - emit sig2(); - else if (x == 3) - emit sig3(); - else - emit otherSig(); -})delim"; - expected = R"delim( -void MyObject::f() -{ - if (x == 1) { - emit sig1(); - } else if (x == 2) { - emit sig2(); - } else if (x == 3) { - emit sig3(); - } else { - emit otherSig(); - } -})delim"; - QTest::newRow("if-else chain, unbraced") << original << expected; - - original = R"delim( -void MyObject::f() -{ - @if (x == 1) { - emit sig1(); - } else if (x == 2) - emit sig2(); - else if (x == 3) - emit sig3(); - else - emit otherSig(); -})delim"; - expected = R"delim( -void MyObject::f() -{ - if (x == 1) { - emit sig1(); - } else if (x == 2) { - emit sig2(); - } else if (x == 3) { - emit sig3(); - } else { - emit otherSig(); - } -})delim"; - QTest::newRow("if-else chain, partially braced 1") << original << expected; - - original = R"delim( -void MyObject::f() -{ - @if (x == 1) - emit sig1(); - else if (x == 2) { - emit sig2(); - } else if (x == 3) - emit sig3(); - else - emit otherSig(); -})delim"; - expected = R"delim( -void MyObject::f() -{ - if (x == 1) { - emit sig1(); - } else if (x == 2) { - emit sig2(); - } else if (x == 3) { - emit sig3(); - } else { - emit otherSig(); - } -})delim"; - QTest::newRow("if-else chain, partially braced 2") << original << expected; - - original = R"delim( -void MyObject::f() -{ - @if (x == 1) - emit sig1(); - else if (x == 2) - emit sig2(); - else if (x == 3) { - emit sig3(); - } else - emit otherSig(); -})delim"; - expected = R"delim( -void MyObject::f() -{ - if (x == 1) { - emit sig1(); - } else if (x == 2) { - emit sig2(); - } else if (x == 3) { - emit sig3(); - } else { - emit otherSig(); - } -})delim"; - QTest::newRow("if-else chain, partially braced 3") << original << expected; - - original = R"delim( -void MyObject::f() -{ - @if (x == 1) - emit sig1(); - else if (x == 2) - emit sig2(); - else if (x == 3) - emit sig3(); - else { - emit otherSig(); - } -})delim"; - expected = R"delim( -void MyObject::f() -{ - if (x == 1) { - emit sig1(); - } else if (x == 2) { - emit sig2(); - } else if (x == 3) { - emit sig3(); - } else { - emit otherSig(); - } -})delim"; - QTest::newRow("if-else chain, partially braced 4") << original << expected; - - original = R"delim( -void MyObject::f() -{ - @if (x == 1) { - emit sig1(); - } else if (x == 2) { - emit sig2(); - } else if (x == 3) { - emit sig3(); - } else { - emit otherSig(); - } -})delim"; - expected.clear(); - QTest::newRow("if-else chain, fully braced") << original << expected; - - original = R"delim( -void MyObject::f() -{ - @while (true) - emit mySig(); -})delim"; - expected = R"delim( -void MyObject::f() -{ - while (true) { - emit mySig(); - } -})delim"; - QTest::newRow("while") << original << expected; - - original = R"delim( -void MyObject::f() -{ - @for (int i = 0; i < 10; ++i) - emit mySig(); -})delim"; - expected = R"delim( -void MyObject::f() -{ - for (int i = 0; i < 10; ++i) { - emit mySig(); - } -})delim"; - QTest::newRow("for") << original << expected; - - original = R"delim( -void MyObject::f() -{ - @for (int i : list) - emit mySig(); -})delim"; - expected = R"delim( -void MyObject::f() -{ - for (int i : list) { - emit mySig(); - } -})delim"; - QTest::newRow("range-based for") << original << expected; - - original = R"delim( -void MyObject::f() -{ - @do - emit mySig(); - while (true); -})delim"; - expected = R"delim( -void MyObject::f() -{ - do { - emit mySig(); - } while (true); -})delim"; - QTest::newRow("do") << original << expected; - - original = R"delim( -void MyObject::f() -{ - @do { - emit mySig(); - } while (true); -})delim"; - expected.clear(); - QTest::newRow("already has braces") << original << expected; -} - -void QuickfixTest::testAddCurlyBraces() -{ - QFETCH(QByteArray, original); - QFETCH(QByteArray, expected); - - AddBracesToControlStatement factory; - QuickFixOperationTest({CppTestDocument::create("file.cpp", original, expected)}, &factory); -} - void QuickfixTest::testChangeCommentType_data() { QTest::addColumn("input"); diff --git a/src/plugins/cppeditor/quickfixes/cppquickfix_test.h b/src/plugins/cppeditor/quickfixes/cppquickfix_test.h index a09051c162e..77b83a2bb12 100644 --- a/src/plugins/cppeditor/quickfixes/cppquickfix_test.h +++ b/src/plugins/cppeditor/quickfixes/cppquickfix_test.h @@ -108,9 +108,6 @@ private slots: void testExtractLiteralAsParameterMemberFunctionSeparateFiles(); void testExtractLiteralAsParameterNotTriggeringForInvalidCode(); - void testAddCurlyBraces_data(); - void testAddCurlyBraces(); - void testChangeCommentType_data(); void testChangeCommentType(); diff --git a/src/plugins/cppeditor/quickfixes/cppquickfixes.cpp b/src/plugins/cppeditor/quickfixes/cppquickfixes.cpp index cf6ddf6a8df..bd15a9c9511 100644 --- a/src/plugins/cppeditor/quickfixes/cppquickfixes.cpp +++ b/src/plugins/cppeditor/quickfixes/cppquickfixes.cpp @@ -28,6 +28,7 @@ #include "moveclasstoownfile.h" #include "movefunctiondefinition.h" #include "removeusingnamespace.h" +#include "rewritecontrolstatements.h" #include #include @@ -237,430 +238,6 @@ void SplitSimpleDeclaration::doMatch(const CppQuickFixInterface &interface, } } -namespace { -template Statement *asControlStatement(AST *node) -{ - if constexpr (std::is_same_v) - return node->asIfStatement(); - if constexpr (std::is_same_v) - return node->asWhileStatement(); - if constexpr (std::is_same_v) - return node->asForStatement(); - if constexpr (std::is_same_v) - return node->asRangeBasedForStatement(); - if constexpr (std::is_same_v) - return node->asDoStatement(); - return nullptr; -} - -template -int triggerToken(const Statement *statement) -{ - if constexpr (std::is_same_v) - return statement->if_token; - if constexpr (std::is_same_v) - return statement->while_token; - if constexpr (std::is_same_v) - return statement->do_token; - if constexpr (std::is_same_v - || std::is_same_v) { - return statement->for_token; - } -} - -template -int tokenToInsertOpeningBraceAfter(const Statement *statement) -{ - if constexpr (std::is_same_v) - return statement->do_token; - return statement->rparen_token; -} - -template class AddBracesToControlStatementOp : public CppQuickFixOperation -{ -public: - AddBracesToControlStatementOp(const CppQuickFixInterface &interface, - const QList &statements, - StatementAST *elseStatement, - int elseToken) - : CppQuickFixOperation(interface, 0) - , m_statements(statements), m_elseStatement(elseStatement), m_elseToken(elseToken) - { - setDescription(Tr::tr("Add Curly Braces")); - } - - void perform() override - { - CppRefactoringChanges refactoring(snapshot()); - CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath()); - - ChangeSet changes; - for (Statement * const statement : m_statements) { - const int start = currentFile->endOf(tokenToInsertOpeningBraceAfter(statement)); - changes.insert(start, QLatin1String(" {")); - if constexpr (std::is_same_v) { - const int end = currentFile->startOf(statement->while_token); - changes.insert(end, QLatin1String("} ")); - } else if constexpr (std::is_same_v) { - if (statement->else_statement) { - changes.insert(currentFile->startOf(statement->else_token), "} "); - } else { - changes.insert(currentFile->endOf(statement->statement->lastToken() - 1), - "\n}"); - } - - } else { - const int end = currentFile->endOf(statement->statement->lastToken() - 1); - changes.insert(end, QLatin1String("\n}")); - } - } - if (m_elseStatement) { - changes.insert(currentFile->endOf(m_elseToken), " {"); - changes.insert(currentFile->endOf(m_elseStatement->lastToken() - 1), "\n}"); - } - - currentFile->setChangeSet(changes); - currentFile->apply(); - } - -private: - const QList m_statements; - StatementAST * const m_elseStatement; - const int m_elseToken; -}; - -} // anonymous namespace - -template -bool checkControlStatementsHelper(const CppQuickFixInterface &interface, QuickFixOperations &result) -{ - Statement * const statement = asControlStatement(interface.path().last()); - if (!statement) - return false; - - QList statements; - if (interface.isCursorOn(triggerToken(statement)) && statement->statement - && !statement->statement->asCompoundStatement()) { - statements << statement; - } - - StatementAST *elseStmt = nullptr; - int elseToken = 0; - if constexpr (std::is_same_v) { - IfStatementAST *currentIfStmt = statement; - for (elseStmt = currentIfStmt->else_statement, elseToken = currentIfStmt->else_token; - elseStmt && (currentIfStmt = elseStmt->asIfStatement()); - elseStmt = currentIfStmt->else_statement, elseToken = currentIfStmt->else_token) { - if (currentIfStmt->statement && !currentIfStmt->statement->asCompoundStatement()) - statements << currentIfStmt; - } - if (elseStmt && (elseStmt->asIfStatement() || elseStmt->asCompoundStatement())) { - elseStmt = nullptr; - elseToken = 0; - } - } - - if (!statements.isEmpty() || elseStmt) - result << new AddBracesToControlStatementOp(interface, statements, elseStmt, elseToken); - return true; -} - -template -void checkControlStatements(const CppQuickFixInterface &interface, QuickFixOperations &result) -{ - (... || checkControlStatementsHelper(interface, result)); -} - -void AddBracesToControlStatement::doMatch(const CppQuickFixInterface &interface, - QuickFixOperations &result) -{ - if (interface.path().isEmpty()) - return; - checkControlStatements(interface, result); -} - -namespace { - -class MoveDeclarationOutOfIfOp: public CppQuickFixOperation -{ -public: - MoveDeclarationOutOfIfOp(const CppQuickFixInterface &interface) - : CppQuickFixOperation(interface) - { - setDescription(Tr::tr("Move Declaration out of Condition")); - - reset(); - } - - void reset() - { - condition = mk.Condition(); - pattern = mk.IfStatement(condition); - } - - void perform() override - { - CppRefactoringChanges refactoring(snapshot()); - CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath()); - - ChangeSet changes; - - changes.copy(currentFile->range(core), currentFile->startOf(condition)); - - int insertPos = currentFile->startOf(pattern); - changes.move(currentFile->range(condition), insertPos); - changes.insert(insertPos, QLatin1String(";\n")); - - currentFile->setChangeSet(changes); - currentFile->apply(); - } - - ASTMatcher matcher; - ASTPatternBuilder mk; - ConditionAST *condition = nullptr; - IfStatementAST *pattern = nullptr; - CoreDeclaratorAST *core = nullptr; -}; - -} // anonymous namespace - -void MoveDeclarationOutOfIf::doMatch(const CppQuickFixInterface &interface, - QuickFixOperations &result) -{ - const QList &path = interface.path(); - using Ptr = QSharedPointer; - Ptr op(new MoveDeclarationOutOfIfOp(interface)); - - int index = path.size() - 1; - for (; index != -1; --index) { - if (IfStatementAST *statement = path.at(index)->asIfStatement()) { - if (statement->match(op->pattern, &op->matcher) && op->condition->declarator) { - DeclaratorAST *declarator = op->condition->declarator; - op->core = declarator->core_declarator; - if (!op->core) - return; - - if (interface.isCursorOn(op->core)) { - op->setPriority(index); - result.append(op); - return; - } - - op->reset(); - } - } - } -} - -namespace { - -class MoveDeclarationOutOfWhileOp: public CppQuickFixOperation -{ -public: - MoveDeclarationOutOfWhileOp(const CppQuickFixInterface &interface) - : CppQuickFixOperation(interface) - { - setDescription(Tr::tr("Move Declaration out of Condition")); - reset(); - } - - void reset() - { - condition = mk.Condition(); - pattern = mk.WhileStatement(condition); - } - - void perform() override - { - CppRefactoringChanges refactoring(snapshot()); - CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath()); - - ChangeSet changes; - - changes.insert(currentFile->startOf(condition), QLatin1String("(")); - changes.insert(currentFile->endOf(condition), QLatin1String(") != 0")); - - int insertPos = currentFile->startOf(pattern); - const int conditionStart = currentFile->startOf(condition); - changes.move(conditionStart, currentFile->startOf(core), insertPos); - changes.copy(currentFile->range(core), insertPos); - changes.insert(insertPos, QLatin1String(";\n")); - - currentFile->setChangeSet(changes); - currentFile->apply(); - } - - ASTMatcher matcher; - ASTPatternBuilder mk; - ConditionAST *condition = nullptr; - WhileStatementAST *pattern = nullptr; - CoreDeclaratorAST *core = nullptr; -}; - -} // anonymous namespace - -void MoveDeclarationOutOfWhile::doMatch(const CppQuickFixInterface &interface, - QuickFixOperations &result) -{ - const QList &path = interface.path(); - QSharedPointer op(new MoveDeclarationOutOfWhileOp(interface)); - - int index = path.size() - 1; - for (; index != -1; --index) { - if (WhileStatementAST *statement = path.at(index)->asWhileStatement()) { - if (statement->match(op->pattern, &op->matcher) && op->condition->declarator) { - DeclaratorAST *declarator = op->condition->declarator; - op->core = declarator->core_declarator; - - if (!op->core) - return; - - if (!declarator->equal_token) - return; - - if (!declarator->initializer) - return; - - if (interface.isCursorOn(op->core)) { - op->setPriority(index); - result.append(op); - return; - } - - op->reset(); - } - } - } -} - -namespace { - -class SplitIfStatementOp: public CppQuickFixOperation -{ -public: - SplitIfStatementOp(const CppQuickFixInterface &interface, int priority, - IfStatementAST *pattern, BinaryExpressionAST *condition) - : CppQuickFixOperation(interface, priority) - , pattern(pattern) - , condition(condition) - { - setDescription(Tr::tr("Split if Statement")); - } - - void perform() override - { - CppRefactoringChanges refactoring(snapshot()); - CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath()); - - const Token binaryToken = currentFile->tokenAt(condition->binary_op_token); - - if (binaryToken.is(T_AMPER_AMPER)) - splitAndCondition(currentFile); - else - splitOrCondition(currentFile); - } - - void splitAndCondition(CppRefactoringFilePtr currentFile) const - { - ChangeSet changes; - - int startPos = currentFile->startOf(pattern); - changes.insert(startPos, QLatin1String("if (")); - changes.move(currentFile->range(condition->left_expression), startPos); - changes.insert(startPos, QLatin1String(") {\n")); - - const int lExprEnd = currentFile->endOf(condition->left_expression); - changes.remove(lExprEnd, currentFile->startOf(condition->right_expression)); - changes.insert(currentFile->endOf(pattern), QLatin1String("\n}")); - - currentFile->setChangeSet(changes); - currentFile->apply(); - } - - void splitOrCondition(CppRefactoringFilePtr currentFile) const - { - ChangeSet changes; - - StatementAST *ifTrueStatement = pattern->statement; - CompoundStatementAST *compoundStatement = ifTrueStatement->asCompoundStatement(); - - int insertPos = currentFile->endOf(ifTrueStatement); - if (compoundStatement) - changes.insert(insertPos, QLatin1String(" ")); - else - changes.insert(insertPos, QLatin1String("\n")); - changes.insert(insertPos, QLatin1String("else if (")); - - const int rExprStart = currentFile->startOf(condition->right_expression); - changes.move(rExprStart, currentFile->startOf(pattern->rparen_token), insertPos); - changes.insert(insertPos, QLatin1String(")")); - - const int rParenEnd = currentFile->endOf(pattern->rparen_token); - changes.copy(rParenEnd, currentFile->endOf(pattern->statement), insertPos); - - const int lExprEnd = currentFile->endOf(condition->left_expression); - changes.remove(lExprEnd, currentFile->startOf(condition->right_expression)); - - currentFile->setChangeSet(changes); - currentFile->apply(); - } - -private: - IfStatementAST *pattern; - BinaryExpressionAST *condition; -}; - -} // anonymous namespace - -void SplitIfStatement::doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) -{ - IfStatementAST *pattern = nullptr; - const QList &path = interface.path(); - - int index = path.size() - 1; - for (; index != -1; --index) { - AST *node = path.at(index); - if (IfStatementAST *stmt = node->asIfStatement()) { - pattern = stmt; - break; - } - } - - if (!pattern || !pattern->statement) - return; - - unsigned splitKind = 0; - for (++index; index < path.size(); ++index) { - AST *node = path.at(index); - BinaryExpressionAST *condition = node->asBinaryExpression(); - if (!condition) - return; - - Token binaryToken = interface.currentFile()->tokenAt(condition->binary_op_token); - - // only accept a chain of ||s or &&s - no mixing - if (!splitKind) { - splitKind = binaryToken.kind(); - if (splitKind != T_AMPER_AMPER && splitKind != T_PIPE_PIPE) - return; - // we can't reliably split &&s in ifs with an else branch - if (splitKind == T_AMPER_AMPER && pattern->else_statement) - return; - } else if (splitKind != binaryToken.kind()) { - return; - } - - if (interface.isCursorOn(condition->binary_op_token)) { - result << new SplitIfStatementOp(interface, index, pattern, condition); - return; - } - } -} - namespace { class ConvertNumericLiteralOp: public CppQuickFixOperation @@ -2814,187 +2391,6 @@ void AssignToLocalVariable::doMatch(const CppQuickFixInterface &interface, Quick } } -namespace { - -class OptimizeForLoopOperation: public CppQuickFixOperation -{ -public: - OptimizeForLoopOperation(const CppQuickFixInterface &interface, const ForStatementAST *forAst, - const bool optimizePostcrement, const ExpressionAST *expression, - const FullySpecifiedType &type) - : CppQuickFixOperation(interface) - , m_forAst(forAst) - , m_optimizePostcrement(optimizePostcrement) - , m_expression(expression) - , m_type(type) - { - setDescription(Tr::tr("Optimize for-Loop")); - } - - void perform() override - { - QTC_ASSERT(m_forAst, return); - - const Utils::FilePath filePath = currentFile()->filePath(); - const CppRefactoringChanges refactoring(snapshot()); - const CppRefactoringFilePtr file = refactoring.cppFile(filePath); - ChangeSet change; - - // Optimize post (in|de)crement operator to pre (in|de)crement operator - if (m_optimizePostcrement && m_forAst->expression) { - PostIncrDecrAST *incrdecr = m_forAst->expression->asPostIncrDecr(); - if (incrdecr && incrdecr->base_expression && incrdecr->incr_decr_token) { - change.flip(file->range(incrdecr->base_expression), - file->range(incrdecr->incr_decr_token)); - } - } - - // Optimize Condition - int renamePos = -1; - if (m_expression) { - QString varName = QLatin1String("total"); - - if (file->textOf(m_forAst->initializer).length() == 1) { - Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview(); - const QString typeAndName = oo.prettyType(m_type, varName); - renamePos = file->endOf(m_forAst->initializer) - 1 + typeAndName.length(); - change.insert(file->endOf(m_forAst->initializer) - 1, // "-1" because of ";" - typeAndName + QLatin1String(" = ") + file->textOf(m_expression)); - } else { - // Check if varName is already used - if (DeclarationStatementAST *ds = m_forAst->initializer->asDeclarationStatement()) { - if (DeclarationAST *decl = ds->declaration) { - if (SimpleDeclarationAST *sdecl = decl->asSimpleDeclaration()) { - for (;;) { - bool match = false; - for (DeclaratorListAST *it = sdecl->declarator_list; it; - it = it->next) { - if (file->textOf(it->value->core_declarator) == varName) { - varName += QLatin1Char('X'); - match = true; - break; - } - } - if (!match) - break; - } - } - } - } - - renamePos = file->endOf(m_forAst->initializer) + 1; - change.insert(file->endOf(m_forAst->initializer) - 1, // "-1" because of ";" - QLatin1String(", ") + varName + QLatin1String(" = ") - + file->textOf(m_expression)); - } - - ChangeSet::Range exprRange(file->startOf(m_expression), file->endOf(m_expression)); - change.replace(exprRange, varName); - } - - file->setChangeSet(change); - file->apply(); - - // Select variable name and trigger symbol rename - if (renamePos != -1) { - QTextCursor c = file->cursor(); - c.setPosition(renamePos); - editor()->setTextCursor(c); - editor()->renameSymbolUnderCursor(); - c.select(QTextCursor::WordUnderCursor); - editor()->setTextCursor(c); - } - } - -private: - const ForStatementAST *m_forAst; - const bool m_optimizePostcrement; - const ExpressionAST *m_expression; - const FullySpecifiedType m_type; -}; - -} // anonymous namespace - -void OptimizeForLoop::doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) -{ - const QList path = interface.path(); - ForStatementAST *forAst = nullptr; - if (!path.isEmpty()) - forAst = path.last()->asForStatement(); - if (!forAst || !interface.isCursorOn(forAst)) - return; - - // Check for optimizing a postcrement - const CppRefactoringFilePtr file = interface.currentFile(); - bool optimizePostcrement = false; - if (forAst->expression) { - if (PostIncrDecrAST *incrdecr = forAst->expression->asPostIncrDecr()) { - const Token t = file->tokenAt(incrdecr->incr_decr_token); - if (t.is(T_PLUS_PLUS) || t.is(T_MINUS_MINUS)) - optimizePostcrement = true; - } - } - - // Check for optimizing condition - bool optimizeCondition = false; - FullySpecifiedType conditionType; - ExpressionAST *conditionExpression = nullptr; - if (forAst->initializer && forAst->condition) { - if (BinaryExpressionAST *binary = forAst->condition->asBinaryExpression()) { - // Get the expression against which we should evaluate - IdExpressionAST *conditionId = binary->left_expression->asIdExpression(); - if (conditionId) { - conditionExpression = binary->right_expression; - } else { - conditionId = binary->right_expression->asIdExpression(); - conditionExpression = binary->left_expression; - } - - if (conditionId && conditionExpression - && !(conditionExpression->asNumericLiteral() - || conditionExpression->asStringLiteral() - || conditionExpression->asIdExpression() - || conditionExpression->asUnaryExpression())) { - // Determine type of for initializer - FullySpecifiedType initializerType; - if (DeclarationStatementAST *stmt = forAst->initializer->asDeclarationStatement()) { - if (stmt->declaration) { - if (SimpleDeclarationAST *decl = stmt->declaration->asSimpleDeclaration()) { - if (decl->symbols) { - if (Symbol *symbol = decl->symbols->value) - initializerType = symbol->type(); - } - } - } - } - - // Determine type of for condition - TypeOfExpression typeOfExpression; - typeOfExpression.init(interface.semanticInfo().doc, interface.snapshot(), - interface.context().bindings()); - typeOfExpression.setExpandTemplates(true); - Scope *scope = file->scopeAt(conditionId->firstToken()); - const QList conditionItems = typeOfExpression( - conditionId, interface.semanticInfo().doc, scope); - if (!conditionItems.isEmpty()) - conditionType = conditionItems.first().type(); - - if (conditionType.isValid() - && (file->textOf(forAst->initializer) == QLatin1String(";") - || initializerType == conditionType)) { - optimizeCondition = true; - } - } - } - } - - if (optimizePostcrement || optimizeCondition) { - result << new OptimizeForLoopOperation(interface, forAst, optimizePostcrement, - optimizeCondition ? conditionExpression : nullptr, - conditionType); - } -} - void ExtraRefactoringOperations::doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) { @@ -3569,13 +2965,8 @@ void createCppQuickFixes() new ConvertNumericLiteral; - new MoveDeclarationOutOfIf; - new MoveDeclarationOutOfWhile; - - new SplitIfStatement; new SplitSimpleDeclaration; - new AddBracesToControlStatement; new RearrangeParamDeclarationList; new ReformatPointerDeclaration; @@ -3599,8 +2990,7 @@ void createCppQuickFixes() registerConvertStringLiteralQuickfixes(); registerCreateDeclarationFromUseQuickfixes(); registerLogicalOperationQuickfixes(); - - new OptimizeForLoop; + registerRewriteControlStatementQuickfixes(); new ExtraRefactoringOperations; diff --git a/src/plugins/cppeditor/quickfixes/cppquickfixes.h b/src/plugins/cppeditor/quickfixes/cppquickfixes.h index ff7d149ae9d..23837fd211f 100644 --- a/src/plugins/cppeditor/quickfixes/cppquickfixes.h +++ b/src/plugins/cppeditor/quickfixes/cppquickfixes.h @@ -70,67 +70,6 @@ private: const bool m_test; }; -/*! - Replace - if (Type name = foo()) {...} - - With - Type name = foo(); - if (name) {...} - - Activates on: the name of the introduced variable -*/ -class MoveDeclarationOutOfIf: public CppQuickFixFactory -{ -public: - void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override; -}; - -/*! - Replace - while (Type name = foo()) {...} - - With - Type name; - while ((name = foo()) != 0) {...} - - Activates on: the name of the introduced variable -*/ -class MoveDeclarationOutOfWhile: public CppQuickFixFactory -{ -public: - void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override; -}; - -/*! - Replace - if (something && something_else) { - } - - with - if (something) - if (something_else) { - } - } - - and - if (something || something_else) - x; - - with - if (something) - x; - else if (something_else) - x; - - Activates on: && or || -*/ -class SplitIfStatement: public CppQuickFixFactory -{ -public: - void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override; -}; - /*! Rewrite int *a, b; @@ -147,25 +86,6 @@ public: void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override; }; -/*! - Add curly braces to a control statement that doesn't already contain a - compound statement. I.e. - - if (a) - b; - becomes - if (a) { - b; - } - - Activates on: the keyword -*/ -class AddBracesToControlStatement : public CppQuickFixFactory -{ -public: - void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override; -}; - /*! Switches places of the parameter declaration under cursor with the next or the previous one in the parameter declaration list @@ -260,16 +180,6 @@ public: void doMatch(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override; }; -/*! - Optimizes a for loop to avoid permanent condition check and forces to use preincrement - or predecrement operators in the expression of the for loop. - */ -class OptimizeForLoop : public CppQuickFixFactory -{ -public: - void doMatch(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override; -}; - //! Converts C-style to C++-style comments and vice versa class ConvertCommentStyle : public CppQuickFixFactory { diff --git a/src/plugins/cppeditor/quickfixes/rewritecontrolstatements.cpp b/src/plugins/cppeditor/quickfixes/rewritecontrolstatements.cpp new file mode 100644 index 00000000000..0590adc3062 --- /dev/null +++ b/src/plugins/cppeditor/quickfixes/rewritecontrolstatements.cpp @@ -0,0 +1,1342 @@ +// 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 "rewritecontrolstatements.h" + +#include "../cppcodestylesettings.h" +#include "../cppeditortr.h" +#include "../cppeditorwidget.h" +#include "../cpprefactoringchanges.h" +#include "cppquickfix.h" + +#include +#include + +#ifdef WITH_TESTS +#include "cppquickfix_test.h" +#include +#endif + +using namespace CPlusPlus; +using namespace TextEditor; +using namespace Utils; + +namespace CppEditor::Internal { +namespace { + +template Statement *asControlStatement(AST *node) +{ + if constexpr (std::is_same_v) + return node->asIfStatement(); + if constexpr (std::is_same_v) + return node->asWhileStatement(); + if constexpr (std::is_same_v) + return node->asForStatement(); + if constexpr (std::is_same_v) + return node->asRangeBasedForStatement(); + if constexpr (std::is_same_v) + return node->asDoStatement(); + return nullptr; +} + +template +int triggerToken(const Statement *statement) +{ + if constexpr (std::is_same_v) + return statement->if_token; + if constexpr (std::is_same_v) + return statement->while_token; + if constexpr (std::is_same_v) + return statement->do_token; + if constexpr (std::is_same_v + || std::is_same_v) { + return statement->for_token; + } +} + +template +int tokenToInsertOpeningBraceAfter(const Statement *statement) +{ + if constexpr (std::is_same_v) + return statement->do_token; + return statement->rparen_token; +} + +template class AddBracesToControlStatementOp : public CppQuickFixOperation +{ +public: + AddBracesToControlStatementOp(const CppQuickFixInterface &interface, + const QList &statements, + StatementAST *elseStatement, + int elseToken) + : CppQuickFixOperation(interface, 0) + , m_statements(statements), m_elseStatement(elseStatement), m_elseToken(elseToken) + { + setDescription(Tr::tr("Add Curly Braces")); + } + + void perform() override + { + CppRefactoringChanges refactoring(snapshot()); + CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath()); + + ChangeSet changes; + for (Statement * const statement : m_statements) { + const int start = currentFile->endOf(tokenToInsertOpeningBraceAfter(statement)); + changes.insert(start, QLatin1String(" {")); + if constexpr (std::is_same_v) { + const int end = currentFile->startOf(statement->while_token); + changes.insert(end, QLatin1String("} ")); + } else if constexpr (std::is_same_v) { + if (statement->else_statement) { + changes.insert(currentFile->startOf(statement->else_token), "} "); + } else { + changes.insert(currentFile->endOf(statement->statement->lastToken() - 1), + "\n}"); + } + + } else { + const int end = currentFile->endOf(statement->statement->lastToken() - 1); + changes.insert(end, QLatin1String("\n}")); + } + } + if (m_elseStatement) { + changes.insert(currentFile->endOf(m_elseToken), " {"); + changes.insert(currentFile->endOf(m_elseStatement->lastToken() - 1), "\n}"); + } + + currentFile->setChangeSet(changes); + currentFile->apply(); + } + +private: + const QList m_statements; + StatementAST * const m_elseStatement; + const int m_elseToken; +}; + +template +bool checkControlStatementsHelper(const CppQuickFixInterface &interface, QuickFixOperations &result) +{ + Statement * const statement = asControlStatement(interface.path().last()); + if (!statement) + return false; + + QList statements; + if (interface.isCursorOn(triggerToken(statement)) && statement->statement + && !statement->statement->asCompoundStatement()) { + statements << statement; + } + + StatementAST *elseStmt = nullptr; + int elseToken = 0; + if constexpr (std::is_same_v) { + IfStatementAST *currentIfStmt = statement; + for (elseStmt = currentIfStmt->else_statement, elseToken = currentIfStmt->else_token; + elseStmt && (currentIfStmt = elseStmt->asIfStatement()); + elseStmt = currentIfStmt->else_statement, elseToken = currentIfStmt->else_token) { + if (currentIfStmt->statement && !currentIfStmt->statement->asCompoundStatement()) + statements << currentIfStmt; + } + if (elseStmt && (elseStmt->asIfStatement() || elseStmt->asCompoundStatement())) { + elseStmt = nullptr; + elseToken = 0; + } + } + + if (!statements.isEmpty() || elseStmt) + result << new AddBracesToControlStatementOp(interface, statements, elseStmt, elseToken); + return true; +} + +template +void checkControlStatements(const CppQuickFixInterface &interface, QuickFixOperations &result) +{ + (... || checkControlStatementsHelper(interface, result)); +} + +class MoveDeclarationOutOfIfOp: public CppQuickFixOperation +{ +public: + MoveDeclarationOutOfIfOp(const CppQuickFixInterface &interface) + : CppQuickFixOperation(interface) + { + setDescription(Tr::tr("Move Declaration out of Condition")); + + reset(); + } + + void reset() + { + condition = mk.Condition(); + pattern = mk.IfStatement(condition); + } + + void perform() override + { + CppRefactoringChanges refactoring(snapshot()); + CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath()); + + ChangeSet changes; + + changes.copy(currentFile->range(core), currentFile->startOf(condition)); + + int insertPos = currentFile->startOf(pattern); + changes.move(currentFile->range(condition), insertPos); + changes.insert(insertPos, QLatin1String(";\n")); + + currentFile->setChangeSet(changes); + currentFile->apply(); + } + + ASTMatcher matcher; + ASTPatternBuilder mk; + ConditionAST *condition = nullptr; + IfStatementAST *pattern = nullptr; + CoreDeclaratorAST *core = nullptr; +}; + +class MoveDeclarationOutOfWhileOp: public CppQuickFixOperation +{ +public: + MoveDeclarationOutOfWhileOp(const CppQuickFixInterface &interface) + : CppQuickFixOperation(interface) + { + setDescription(Tr::tr("Move Declaration out of Condition")); + reset(); + } + + void reset() + { + condition = mk.Condition(); + pattern = mk.WhileStatement(condition); + } + + void perform() override + { + CppRefactoringChanges refactoring(snapshot()); + CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath()); + + ChangeSet changes; + + changes.insert(currentFile->startOf(condition), QLatin1String("(")); + changes.insert(currentFile->endOf(condition), QLatin1String(") != 0")); + + int insertPos = currentFile->startOf(pattern); + const int conditionStart = currentFile->startOf(condition); + changes.move(conditionStart, currentFile->startOf(core), insertPos); + changes.copy(currentFile->range(core), insertPos); + changes.insert(insertPos, QLatin1String(";\n")); + + currentFile->setChangeSet(changes); + currentFile->apply(); + } + + ASTMatcher matcher; + ASTPatternBuilder mk; + ConditionAST *condition = nullptr; + WhileStatementAST *pattern = nullptr; + CoreDeclaratorAST *core = nullptr; +}; + +class SplitIfStatementOp: public CppQuickFixOperation +{ +public: + SplitIfStatementOp(const CppQuickFixInterface &interface, int priority, + IfStatementAST *pattern, BinaryExpressionAST *condition) + : CppQuickFixOperation(interface, priority) + , pattern(pattern) + , condition(condition) + { + setDescription(Tr::tr("Split if Statement")); + } + + void perform() override + { + CppRefactoringChanges refactoring(snapshot()); + CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath()); + + const Token binaryToken = currentFile->tokenAt(condition->binary_op_token); + + if (binaryToken.is(T_AMPER_AMPER)) + splitAndCondition(currentFile); + else + splitOrCondition(currentFile); + } + + void splitAndCondition(CppRefactoringFilePtr currentFile) const + { + ChangeSet changes; + + int startPos = currentFile->startOf(pattern); + changes.insert(startPos, QLatin1String("if (")); + changes.move(currentFile->range(condition->left_expression), startPos); + changes.insert(startPos, QLatin1String(") {\n")); + + const int lExprEnd = currentFile->endOf(condition->left_expression); + changes.remove(lExprEnd, currentFile->startOf(condition->right_expression)); + changes.insert(currentFile->endOf(pattern), QLatin1String("\n}")); + + currentFile->setChangeSet(changes); + currentFile->apply(); + } + + void splitOrCondition(CppRefactoringFilePtr currentFile) const + { + ChangeSet changes; + + StatementAST *ifTrueStatement = pattern->statement; + CompoundStatementAST *compoundStatement = ifTrueStatement->asCompoundStatement(); + + int insertPos = currentFile->endOf(ifTrueStatement); + if (compoundStatement) + changes.insert(insertPos, QLatin1String(" ")); + else + changes.insert(insertPos, QLatin1String("\n")); + changes.insert(insertPos, QLatin1String("else if (")); + + const int rExprStart = currentFile->startOf(condition->right_expression); + changes.move(rExprStart, currentFile->startOf(pattern->rparen_token), insertPos); + changes.insert(insertPos, QLatin1String(")")); + + const int rParenEnd = currentFile->endOf(pattern->rparen_token); + changes.copy(rParenEnd, currentFile->endOf(pattern->statement), insertPos); + + const int lExprEnd = currentFile->endOf(condition->left_expression); + changes.remove(lExprEnd, currentFile->startOf(condition->right_expression)); + + currentFile->setChangeSet(changes); + currentFile->apply(); + } + +private: + IfStatementAST *pattern; + BinaryExpressionAST *condition; +}; + +class OptimizeForLoopOperation: public CppQuickFixOperation +{ +public: + OptimizeForLoopOperation(const CppQuickFixInterface &interface, const ForStatementAST *forAst, + const bool optimizePostcrement, const ExpressionAST *expression, + const FullySpecifiedType &type) + : CppQuickFixOperation(interface) + , m_forAst(forAst) + , m_optimizePostcrement(optimizePostcrement) + , m_expression(expression) + , m_type(type) + { + setDescription(Tr::tr("Optimize for-Loop")); + } + + void perform() override + { + QTC_ASSERT(m_forAst, return); + + const Utils::FilePath filePath = currentFile()->filePath(); + const CppRefactoringChanges refactoring(snapshot()); + const CppRefactoringFilePtr file = refactoring.cppFile(filePath); + ChangeSet change; + + // Optimize post (in|de)crement operator to pre (in|de)crement operator + if (m_optimizePostcrement && m_forAst->expression) { + PostIncrDecrAST *incrdecr = m_forAst->expression->asPostIncrDecr(); + if (incrdecr && incrdecr->base_expression && incrdecr->incr_decr_token) { + change.flip(file->range(incrdecr->base_expression), + file->range(incrdecr->incr_decr_token)); + } + } + + // Optimize Condition + int renamePos = -1; + if (m_expression) { + QString varName = QLatin1String("total"); + + if (file->textOf(m_forAst->initializer).length() == 1) { + Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview(); + const QString typeAndName = oo.prettyType(m_type, varName); + renamePos = file->endOf(m_forAst->initializer) - 1 + typeAndName.length(); + change.insert(file->endOf(m_forAst->initializer) - 1, // "-1" because of ";" + typeAndName + QLatin1String(" = ") + file->textOf(m_expression)); + } else { + // Check if varName is already used + if (DeclarationStatementAST *ds = m_forAst->initializer->asDeclarationStatement()) { + if (DeclarationAST *decl = ds->declaration) { + if (SimpleDeclarationAST *sdecl = decl->asSimpleDeclaration()) { + for (;;) { + bool match = false; + for (DeclaratorListAST *it = sdecl->declarator_list; it; + it = it->next) { + if (file->textOf(it->value->core_declarator) == varName) { + varName += QLatin1Char('X'); + match = true; + break; + } + } + if (!match) + break; + } + } + } + } + + renamePos = file->endOf(m_forAst->initializer) + 1; + change.insert(file->endOf(m_forAst->initializer) - 1, // "-1" because of ";" + QLatin1String(", ") + varName + QLatin1String(" = ") + + file->textOf(m_expression)); + } + + ChangeSet::Range exprRange(file->startOf(m_expression), file->endOf(m_expression)); + change.replace(exprRange, varName); + } + + file->setChangeSet(change); + file->apply(); + + // Select variable name and trigger symbol rename + if (renamePos != -1) { + QTextCursor c = file->cursor(); + c.setPosition(renamePos); + editor()->setTextCursor(c); + editor()->renameSymbolUnderCursor(); + c.select(QTextCursor::WordUnderCursor); + editor()->setTextCursor(c); + } + } + +private: + const ForStatementAST *m_forAst; + const bool m_optimizePostcrement; + const ExpressionAST *m_expression; + const FullySpecifiedType m_type; +}; + +/*! + Replace + if (Type name = foo()) {...} + + With + Type name = foo(); + if (name) {...} + + Activates on: the name of the introduced variable +*/ +class MoveDeclarationOutOfIf: public CppQuickFixFactory +{ +#ifdef WITH_TESTS +public: + static QObject *createTest(); +#endif + +private: + void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override + { + const QList &path = interface.path(); + using Ptr = QSharedPointer; + Ptr op(new MoveDeclarationOutOfIfOp(interface)); + + int index = path.size() - 1; + for (; index != -1; --index) { + if (IfStatementAST *statement = path.at(index)->asIfStatement()) { + if (statement->match(op->pattern, &op->matcher) && op->condition->declarator) { + DeclaratorAST *declarator = op->condition->declarator; + op->core = declarator->core_declarator; + if (!op->core) + return; + + if (interface.isCursorOn(op->core)) { + op->setPriority(index); + result.append(op); + return; + } + + op->reset(); + } + } + } + } +}; + +/*! + Replace + while (Type name = foo()) {...} + + With + Type name; + while ((name = foo()) != 0) {...} + + Activates on: the name of the introduced variable +*/ +class MoveDeclarationOutOfWhile: public CppQuickFixFactory +{ +#ifdef WITH_TESTS +public: + static QObject *createTest(); +#endif + +private: + void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override + { + const QList &path = interface.path(); + QSharedPointer op(new MoveDeclarationOutOfWhileOp(interface)); + + int index = path.size() - 1; + for (; index != -1; --index) { + if (WhileStatementAST *statement = path.at(index)->asWhileStatement()) { + if (statement->match(op->pattern, &op->matcher) && op->condition->declarator) { + DeclaratorAST *declarator = op->condition->declarator; + op->core = declarator->core_declarator; + + if (!op->core) + return; + + if (!declarator->equal_token) + return; + + if (!declarator->initializer) + return; + + if (interface.isCursorOn(op->core)) { + op->setPriority(index); + result.append(op); + return; + } + + op->reset(); + } + } + } + } +}; + +/*! + Replace + if (something && something_else) { + } + + with + if (something) + if (something_else) { + } + } + + and + if (something || something_else) + x; + + with + if (something) + x; + else if (something_else) + x; + + Activates on: && or || +*/ +class SplitIfStatement: public CppQuickFixFactory +{ +#ifdef WITH_TESTS +public: + static QObject *createTest() { return new QObject; } +#endif + +private: + void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override + { + IfStatementAST *pattern = nullptr; + const QList &path = interface.path(); + + int index = path.size() - 1; + for (; index != -1; --index) { + AST *node = path.at(index); + if (IfStatementAST *stmt = node->asIfStatement()) { + pattern = stmt; + break; + } + } + + if (!pattern || !pattern->statement) + return; + + unsigned splitKind = 0; + for (++index; index < path.size(); ++index) { + AST *node = path.at(index); + BinaryExpressionAST *condition = node->asBinaryExpression(); + if (!condition) + return; + + Token binaryToken = interface.currentFile()->tokenAt(condition->binary_op_token); + + // only accept a chain of ||s or &&s - no mixing + if (!splitKind) { + splitKind = binaryToken.kind(); + if (splitKind != T_AMPER_AMPER && splitKind != T_PIPE_PIPE) + return; + // we can't reliably split &&s in ifs with an else branch + if (splitKind == T_AMPER_AMPER && pattern->else_statement) + return; + } else if (splitKind != binaryToken.kind()) { + return; + } + + if (interface.isCursorOn(condition->binary_op_token)) { + result << new SplitIfStatementOp(interface, index, pattern, condition); + return; + } + } + } +}; + +/*! + Add curly braces to a control statement that doesn't already contain a + compound statement. I.e. + + if (a) + b; + becomes + if (a) { + b; + } + + Activates on: the keyword +*/ +class AddBracesToControlStatement : public CppQuickFixFactory +{ +#ifdef WITH_TESTS +public: + static QObject *createTest(); +#endif + +private: + void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override + { + if (interface.path().isEmpty()) + return; + checkControlStatements(interface, result); + } +}; + +/*! + Optimizes a for loop to avoid permanent condition check and forces to use preincrement + or predecrement operators in the expression of the for loop. + */ +class OptimizeForLoop : public CppQuickFixFactory +{ +#ifdef WITH_TESTS +public: + static QObject *createTest(); +#endif + +private: + void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override + { + const QList path = interface.path(); + ForStatementAST *forAst = nullptr; + if (!path.isEmpty()) + forAst = path.last()->asForStatement(); + if (!forAst || !interface.isCursorOn(forAst)) + return; + + // Check for optimizing a postcrement + const CppRefactoringFilePtr file = interface.currentFile(); + bool optimizePostcrement = false; + if (forAst->expression) { + if (PostIncrDecrAST *incrdecr = forAst->expression->asPostIncrDecr()) { + const Token t = file->tokenAt(incrdecr->incr_decr_token); + if (t.is(T_PLUS_PLUS) || t.is(T_MINUS_MINUS)) + optimizePostcrement = true; + } + } + + // Check for optimizing condition + bool optimizeCondition = false; + FullySpecifiedType conditionType; + ExpressionAST *conditionExpression = nullptr; + if (forAst->initializer && forAst->condition) { + if (BinaryExpressionAST *binary = forAst->condition->asBinaryExpression()) { + // Get the expression against which we should evaluate + IdExpressionAST *conditionId = binary->left_expression->asIdExpression(); + if (conditionId) { + conditionExpression = binary->right_expression; + } else { + conditionId = binary->right_expression->asIdExpression(); + conditionExpression = binary->left_expression; + } + + if (conditionId && conditionExpression + && !(conditionExpression->asNumericLiteral() + || conditionExpression->asStringLiteral() + || conditionExpression->asIdExpression() + || conditionExpression->asUnaryExpression())) { + // Determine type of for initializer + FullySpecifiedType initializerType; + if (DeclarationStatementAST *stmt = forAst->initializer->asDeclarationStatement()) { + if (stmt->declaration) { + if (SimpleDeclarationAST *decl = stmt->declaration->asSimpleDeclaration()) { + if (decl->symbols) { + if (Symbol *symbol = decl->symbols->value) + initializerType = symbol->type(); + } + } + } + } + + // Determine type of for condition + TypeOfExpression typeOfExpression; + typeOfExpression.init(interface.semanticInfo().doc, interface.snapshot(), + interface.context().bindings()); + typeOfExpression.setExpandTemplates(true); + Scope *scope = file->scopeAt(conditionId->firstToken()); + const QList conditionItems = typeOfExpression( + conditionId, interface.semanticInfo().doc, scope); + if (!conditionItems.isEmpty()) + conditionType = conditionItems.first().type(); + + if (conditionType.isValid() + && (file->textOf(forAst->initializer) == QLatin1String(";") + || initializerType == conditionType)) { + optimizeCondition = true; + } + } + } + } + + if (optimizePostcrement || optimizeCondition) { + result << new OptimizeForLoopOperation(interface, forAst, optimizePostcrement, + optimizeCondition ? conditionExpression : nullptr, + conditionType); + } + } +}; + +#ifdef WITH_TESTS +using namespace Tests; + +class MoveDeclarationOutOfIfTest : public QObject +{ + Q_OBJECT + +private slots: + void test_data() + { + QTest::addColumn("original"); + QTest::addColumn("expected"); + + QTest::newRow("ifOnly") + << QByteArray( + "void f()\n" + "{\n" + " if (Foo *@foo = g())\n" + " h();\n" + "}\n") + << QByteArray( + "void f()\n" + "{\n" + " Foo *foo = g();\n" + " if (foo)\n" + " h();\n" + "}\n"); + QTest::newRow("ifElse") + << QByteArray( + "void f()\n" + "{\n" + " if (Foo *@foo = g())\n" + " h();\n" + " else\n" + " i();\n" + "}\n") + << QByteArray( + "void f()\n" + "{\n" + " Foo *foo = g();\n" + " if (foo)\n" + " h();\n" + " else\n" + " i();\n" + "}\n"); + + QTest::newRow("MoveDeclarationOutOfIf_ifElseIf") + << QByteArray( + "void f()\n" + "{\n" + " if (Foo *foo = g()) {\n" + " if (Bar *@bar = x()) {\n" + " h();\n" + " j();\n" + " }\n" + " } else {\n" + " i();\n" + " }\n" + "}\n") + << QByteArray( + "void f()\n" + "{\n" + " if (Foo *foo = g()) {\n" + " Bar *bar = x();\n" + " if (bar) {\n" + " h();\n" + " j();\n" + " }\n" + " } else {\n" + " i();\n" + " }\n" + "}\n"); + } + + void test() + { + QFETCH(QByteArray, original); + QFETCH(QByteArray, expected); + MoveDeclarationOutOfIf factory; + QuickFixOperationTest(singleDocument(original, expected), &factory); + } +}; + +class MoveDeclarationOutOfWhileTest : public QObject +{ + Q_OBJECT + +private slots: + void test_data() + { + QTest::addColumn("original"); + QTest::addColumn("expected"); + + QTest::newRow("singleWhile") + << QByteArray( + "void f()\n" + "{\n" + " while (Foo *@foo = g())\n" + " j();\n" + "}\n") + << QByteArray( + "void f()\n" + "{\n" + " Foo *foo;\n" + " while ((foo = g()) != 0)\n" + " j();\n" + "}\n"); + QTest::newRow("whileInWhile") + << QByteArray( + "void f()\n" + "{\n" + " while (Foo *foo = g()) {\n" + " while (Bar *@bar = h()) {\n" + " i();\n" + " j();\n" + " }\n" + " }\n" + "}\n") + << QByteArray( + "void f()\n" + "{\n" + " while (Foo *foo = g()) {\n" + " Bar *bar;\n" + " while ((bar = h()) != 0) {\n" + " i();\n" + " j();\n" + " }\n" + " }\n" + "}\n" + ); + + } + + void test() + { + QFETCH(QByteArray, original); + QFETCH(QByteArray, expected); + MoveDeclarationOutOfWhile factory; + QuickFixOperationTest(singleDocument(original, expected), &factory); + } +}; + +class OptimizeForLoopTest : public QObject +{ + Q_OBJECT + +private slots: + void test_data() + { + QTest::addColumn("original"); + QTest::addColumn("expected"); + + // Check: optimize postcrement + QTest::newRow("OptimizeForLoop_postcrement") + << QByteArray("void foo() {f@or (int i = 0; i < 3; i++) {}}\n") + << QByteArray("void foo() {for (int i = 0; i < 3; ++i) {}}\n"); + + // Check: optimize condition + QTest::newRow("OptimizeForLoop_condition") + << QByteArray("void foo() {f@or (int i = 0; i < 3 + 5; ++i) {}}\n") + << QByteArray("void foo() {for (int i = 0, total = 3 + 5; i < total; ++i) {}}\n"); + + // Check: optimize fliped condition + QTest::newRow("OptimizeForLoop_flipedCondition") + << QByteArray("void foo() {f@or (int i = 0; 3 + 5 > i; ++i) {}}\n") + << QByteArray("void foo() {for (int i = 0, total = 3 + 5; total > i; ++i) {}}\n"); + + // Check: if "total" used, create other name. + QTest::newRow("OptimizeForLoop_alterVariableName") + << QByteArray("void foo() {f@or (int i = 0, total = 0; i < 3 + 5; ++i) {}}\n") + << QByteArray("void foo() {for (int i = 0, total = 0, totalX = 3 + 5; i < totalX; ++i) {}}\n"); + + // Check: optimize postcrement and condition + QTest::newRow("OptimizeForLoop_optimizeBoth") + << QByteArray("void foo() {f@or (int i = 0; i < 3 + 5; i++) {}}\n") + << QByteArray("void foo() {for (int i = 0, total = 3 + 5; i < total; ++i) {}}\n"); + + // Check: empty initializier + QTest::newRow("OptimizeForLoop_emptyInitializer") + << QByteArray("int i; void foo() {f@or (; i < 3 + 5; ++i) {}}\n") + << QByteArray("int i; void foo() {for (int total = 3 + 5; i < total; ++i) {}}\n"); + + // Check: wrong initializier type -> no trigger + QTest::newRow("OptimizeForLoop_wrongInitializer") + << QByteArray("int i; void foo() {f@or (double a = 0; i < 3 + 5; ++i) {}}\n") + << QByteArray(); + + // Check: No trigger when numeric + QTest::newRow("OptimizeForLoop_noTriggerNumeric1") + << QByteArray("void foo() {fo@r (int i = 0; i < 3; ++i) {}}\n") + << QByteArray(); + + // Check: No trigger when numeric + QTest::newRow("OptimizeForLoop_noTriggerNumeric2") + << QByteArray("void foo() {fo@r (int i = 0; i < -3; ++i) {}}\n") + << QByteArray(); + } + + void test() + { + QFETCH(QByteArray, original); + QFETCH(QByteArray, expected); + OptimizeForLoop factory; + QuickFixOperationTest(singleDocument(original, expected), &factory); + } +}; + +class AddBracesToControlStatementTest : public QObject +{ + Q_OBJECT + +private slots: + void test_data() + { + QTest::addColumn("original"); + QTest::addColumn("expected"); + + QByteArray original = R"delim( +void MyObject::f() +{ + @if (true) + emit mySig(); +})delim"; + QByteArray expected = R"delim( +void MyObject::f() +{ + if (true) { + emit mySig(); + } +})delim"; + QTest::newRow("if") << original << expected; + + original = R"delim( +void MyObject::f() +{ + @if (true) + emit mySig(); + else + emit otherSig(); +})delim"; + expected = R"delim( +void MyObject::f() +{ + @if (true) { + emit mySig(); + } else { + emit otherSig(); + } +})delim"; + QTest::newRow("if with one else, unbraced") << original << expected; + + original = R"delim( +void MyObject::f() +{ + @if (true) { + emit mySig(); + } else + emit otherSig(); +})delim"; + expected = R"delim( +void MyObject::f() +{ + @if (true) { + emit mySig(); + } else { + emit otherSig(); + } +})delim"; + QTest::newRow("if with one else, if braced") << original << expected; + + original = R"delim( +void MyObject::f() +{ + @if (true) + emit mySig(); + else { + emit otherSig(); + } +})delim"; + expected = R"delim( +void MyObject::f() +{ + @if (true) { + emit mySig(); + } else { + emit otherSig(); + } +})delim"; + QTest::newRow("if with one else, else braced") << original << expected; + + original = R"delim( +void MyObject::f() +{ + @if (true) { + emit mySig(); + } else { + emit otherSig(); + } +})delim"; + expected.clear(); + QTest::newRow("if with one else, both braced") << original << expected; + + original = R"delim( +void MyObject::f() +{ + @if (x == 1) + emit sig1(); + else if (x == 2) + emit sig2(); +})delim"; + expected = R"delim( +void MyObject::f() +{ + if (x == 1) { + emit sig1(); + } else if (x == 2) { + emit sig2(); + } +})delim"; + QTest::newRow("if-else chain without final else, unbraced") << original << expected; + + original = R"delim( +void MyObject::f() +{ + @if (x == 1) { + emit sig1(); + } else if (x == 2) + emit sig2(); +})delim"; + expected = R"delim( +void MyObject::f() +{ + if (x == 1) { + emit sig1(); + } else if (x == 2) { + emit sig2(); + } +})delim"; + QTest::newRow("if-else chain without final else, partially braced 1") << original << expected; + + original = R"delim( +void MyObject::f() +{ + @if (x == 1) + emit sig1(); + else if (x == 2) { + emit sig2(); + } +})delim"; + expected = R"delim( +void MyObject::f() +{ + if (x == 1) { + emit sig1(); + } else if (x == 2) { + emit sig2(); + } +})delim"; + QTest::newRow("if-else chain without final else, partially braced 2") << original << expected; + + original = R"delim( +void MyObject::f() +{ + @if (x == 1) { + emit sig1(); + } else if (x == 2) { + emit sig2(); + } +})delim"; + expected.clear(); + QTest::newRow("if-else chain without final else, fully braced") << original << expected; + + original = R"delim( +void MyObject::f() +{ + @if (x == 1) + emit sig1(); + else if (x == 2) + emit sig2(); + else if (x == 3) + emit sig3(); + else + emit otherSig(); +})delim"; + expected = R"delim( +void MyObject::f() +{ + if (x == 1) { + emit sig1(); + } else if (x == 2) { + emit sig2(); + } else if (x == 3) { + emit sig3(); + } else { + emit otherSig(); + } +})delim"; + QTest::newRow("if-else chain, unbraced") << original << expected; + + original = R"delim( +void MyObject::f() +{ + @if (x == 1) { + emit sig1(); + } else if (x == 2) + emit sig2(); + else if (x == 3) + emit sig3(); + else + emit otherSig(); +})delim"; + expected = R"delim( +void MyObject::f() +{ + if (x == 1) { + emit sig1(); + } else if (x == 2) { + emit sig2(); + } else if (x == 3) { + emit sig3(); + } else { + emit otherSig(); + } +})delim"; + QTest::newRow("if-else chain, partially braced 1") << original << expected; + + original = R"delim( +void MyObject::f() +{ + @if (x == 1) + emit sig1(); + else if (x == 2) { + emit sig2(); + } else if (x == 3) + emit sig3(); + else + emit otherSig(); +})delim"; + expected = R"delim( +void MyObject::f() +{ + if (x == 1) { + emit sig1(); + } else if (x == 2) { + emit sig2(); + } else if (x == 3) { + emit sig3(); + } else { + emit otherSig(); + } +})delim"; + QTest::newRow("if-else chain, partially braced 2") << original << expected; + + original = R"delim( +void MyObject::f() +{ + @if (x == 1) + emit sig1(); + else if (x == 2) + emit sig2(); + else if (x == 3) { + emit sig3(); + } else + emit otherSig(); +})delim"; + expected = R"delim( +void MyObject::f() +{ + if (x == 1) { + emit sig1(); + } else if (x == 2) { + emit sig2(); + } else if (x == 3) { + emit sig3(); + } else { + emit otherSig(); + } +})delim"; + QTest::newRow("if-else chain, partially braced 3") << original << expected; + + original = R"delim( +void MyObject::f() +{ + @if (x == 1) + emit sig1(); + else if (x == 2) + emit sig2(); + else if (x == 3) + emit sig3(); + else { + emit otherSig(); + } +})delim"; + expected = R"delim( +void MyObject::f() +{ + if (x == 1) { + emit sig1(); + } else if (x == 2) { + emit sig2(); + } else if (x == 3) { + emit sig3(); + } else { + emit otherSig(); + } +})delim"; + QTest::newRow("if-else chain, partially braced 4") << original << expected; + + original = R"delim( +void MyObject::f() +{ + @if (x == 1) { + emit sig1(); + } else if (x == 2) { + emit sig2(); + } else if (x == 3) { + emit sig3(); + } else { + emit otherSig(); + } +})delim"; + expected.clear(); + QTest::newRow("if-else chain, fully braced") << original << expected; + + original = R"delim( +void MyObject::f() +{ + @while (true) + emit mySig(); +})delim"; + expected = R"delim( +void MyObject::f() +{ + while (true) { + emit mySig(); + } +})delim"; + QTest::newRow("while") << original << expected; + + original = R"delim( +void MyObject::f() +{ + @for (int i = 0; i < 10; ++i) + emit mySig(); +})delim"; + expected = R"delim( +void MyObject::f() +{ + for (int i = 0; i < 10; ++i) { + emit mySig(); + } +})delim"; + QTest::newRow("for") << original << expected; + + original = R"delim( +void MyObject::f() +{ + @for (int i : list) + emit mySig(); +})delim"; + expected = R"delim( +void MyObject::f() +{ + for (int i : list) { + emit mySig(); + } +})delim"; + QTest::newRow("range-based for") << original << expected; + + original = R"delim( +void MyObject::f() +{ + @do + emit mySig(); + while (true); +})delim"; + expected = R"delim( +void MyObject::f() +{ + do { + emit mySig(); + } while (true); +})delim"; + QTest::newRow("do") << original << expected; + + original = R"delim( +void MyObject::f() +{ + @do { + emit mySig(); + } while (true); +})delim"; + expected.clear(); + QTest::newRow("already has braces") << original << expected; + } + + void test() + { + QFETCH(QByteArray, original); + QFETCH(QByteArray, expected); + + AddBracesToControlStatement factory; + QuickFixOperationTest({CppTestDocument::create("file.cpp", original, expected)}, &factory); + } +}; + +QObject *MoveDeclarationOutOfIf::createTest() { return new MoveDeclarationOutOfIfTest; } +QObject *MoveDeclarationOutOfWhile::createTest() { return new MoveDeclarationOutOfWhileTest; } +QObject *OptimizeForLoop::createTest() { return new OptimizeForLoopTest; } +QObject *AddBracesToControlStatement::createTest() { return new AddBracesToControlStatementTest; } + +#endif // WITH_TESTS +} // namespace + +void registerRewriteControlStatementQuickfixes() +{ + CppQuickFixFactory::registerFactory(); + CppQuickFixFactory::registerFactory(); + CppQuickFixFactory::registerFactory(); + CppQuickFixFactory::registerFactory(); + CppQuickFixFactory::registerFactory(); +} + +} // namespace CppEditor::Internal + +#ifdef WITH_TESTS +#include +#endif diff --git a/src/plugins/cppeditor/quickfixes/rewritecontrolstatements.h b/src/plugins/cppeditor/quickfixes/rewritecontrolstatements.h new file mode 100644 index 00000000000..d450708a4fd --- /dev/null +++ b/src/plugins/cppeditor/quickfixes/rewritecontrolstatements.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 registerRewriteControlStatementQuickfixes(); +} // namespace CppEditor::Internal