CppEditor: Add curly braces for more control statements

Fixes: QTCREATORBUG-24542
Change-Id: I3e0893e1c736730d94e2c9ab2baa0aa580393fe4
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
Christian Kandeler
2024-02-07 14:12:11 +01:00
parent 10e2ff5362
commit 837e951b29
4 changed files with 166 additions and 46 deletions

View File

@@ -8229,29 +8229,105 @@ void QuickfixTest::testExtractLiteralAsParameterNotTriggeringForInvalidCode()
QuickFixOperationTest(testDocuments, &factory);
}
void QuickfixTest::testAddCurlyBraces()
void QuickfixTest::testAddCurlyBraces_data()
{
QList<TestDocumentPtr> testDocuments;
const QByteArray original = R"delim(
QTest::addColumn<QByteArray>("original");
QTest::addColumn<QByteArray>("expected");
QByteArray original = R"delim(
void MyObject::f()
{
@if (true)
emit mySig();
}
)delim";
const QByteArray expected = R"delim(
})delim";
QByteArray expected = R"delim(
void MyObject::f()
{
if (true) {
emit mySig();
}
})delim";
QTest::newRow("if") << 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;
}
)delim";
testDocuments << CppTestDocument::create("file.cpp", original, expected);
AddBracesToIf factory;
QuickFixOperationTest(testDocuments, &factory);
void QuickfixTest::testAddCurlyBraces()
{
QFETCH(QByteArray, original);
QFETCH(QByteArray, expected);
AddBracesToControlStatement factory;
QuickFixOperationTest({CppTestDocument::create("file.cpp", original, expected)}, &factory);
}
void QuickfixTest::testConvertQt4ConnectConnectOutOfClass()

View File

@@ -209,6 +209,7 @@ private slots:
void testExtractLiteralAsParameterMemberFunctionSeparateFiles();
void testExtractLiteralAsParameterNotTriggeringForInvalidCode();
void testAddCurlyBraces_data();
void testAddCurlyBraces();
void testRemoveUsingNamespace_data();

View File

@@ -764,13 +764,49 @@ void SplitSimpleDeclaration::doMatch(const CppQuickFixInterface &interface,
}
namespace {
template<typename Statement> Statement *asControlStatement(AST *node)
{
if constexpr (std::is_same_v<Statement, IfStatementAST>)
return node->asIfStatement();
if constexpr (std::is_same_v<Statement, WhileStatementAST>)
return node->asWhileStatement();
if constexpr (std::is_same_v<Statement, ForStatementAST>)
return node->asForStatement();
if constexpr (std::is_same_v<Statement, RangeBasedForStatementAST>)
return node->asRangeBasedForStatement();
if constexpr (std::is_same_v<Statement, DoStatementAST>)
return node->asDoStatement();
return nullptr;
}
class AddBracesToIfOp: public CppQuickFixOperation
template<typename Statement>
int triggerToken(const Statement *statement)
{
if constexpr (std::is_same_v<Statement, IfStatementAST>)
return statement->if_token;
if constexpr (std::is_same_v<Statement, WhileStatementAST>)
return statement->while_token;
if constexpr (std::is_same_v<Statement, DoStatementAST>)
return statement->do_token;
if constexpr (std::is_same_v<Statement, ForStatementAST>
|| std::is_same_v<Statement, RangeBasedForStatementAST>) {
return statement->for_token;
}
}
template<typename Statement>
int tokenToInsertOpeningBraceAfter(const Statement *statement)
{
if constexpr (std::is_same_v<Statement, DoStatementAST>)
return statement->do_token;
return statement->rparen_token;
}
template<typename Statement> class AddBracesToControlStatementOp : public CppQuickFixOperation
{
public:
AddBracesToIfOp(const CppQuickFixInterface &interface, int priority,
const IfStatementAST *statement)
: CppQuickFixOperation(interface, priority)
AddBracesToControlStatementOp(const CppQuickFixInterface &interface, const Statement *statement)
: CppQuickFixOperation(interface, 0)
, _statement(statement)
{
setDescription(Tr::tr("Add Curly Braces"));
@@ -783,51 +819,58 @@ public:
ChangeSet changes;
const int start = currentFile->endOf(_statement->rparen_token);
const int start = currentFile->endOf(tokenToInsertOpeningBraceAfter(_statement));
changes.insert(start, QLatin1String(" {"));
const int end = currentFile->endOf(_statement->statement->lastToken() - 1);
changes.insert(end, QLatin1String("\n}"));
if constexpr (std::is_same_v<Statement, DoStatementAST>) {
const int end = currentFile->startOf(_statement->while_token);
changes.insert(end, QLatin1String("} "));
} else {
const int end = currentFile->endOf(_statement->statement->lastToken() - 1);
changes.insert(end, QLatin1String("\n}"));
}
// TODO: For if statements, also bracify all else cases.
// Also check all else cases in the factory.
currentFile->setChangeSet(changes);
currentFile->apply();
}
private:
const IfStatementAST * const _statement;
const Statement * const _statement;
};
} // anonymous namespace
void AddBracesToIf::doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result)
template<typename Statement>
bool checkControlStatementsHelper(const CppQuickFixInterface &interface, QuickFixOperations &result)
{
const QList<AST *> &path = interface.path();
if (path.isEmpty())
return;
Statement * const statement = asControlStatement<Statement>(interface.path().last());
if (!statement)
return false;
// show when we're on the 'if' of an if statement
int index = path.size() - 1;
IfStatementAST *ifStatement = path.at(index)->asIfStatement();
if (ifStatement && interface.isCursorOn(ifStatement->if_token) && ifStatement->statement
&& !ifStatement->statement->asCompoundStatement()) {
result << new AddBracesToIfOp(interface, index, ifStatement);
return;
if (interface.isCursorOn(triggerToken(statement)) && statement->statement
&& !statement->statement->asCompoundStatement()) {
result << new AddBracesToControlStatementOp(interface, statement);
}
return true;
}
// or if we're on the statement contained in the if
// ### This may not be such a good idea, consider nested ifs...
for (; index != -1; --index) {
IfStatementAST *ifStatement = path.at(index)->asIfStatement();
if (ifStatement && ifStatement->statement
&& interface.isCursorOn(ifStatement->statement)
&& !ifStatement->statement->asCompoundStatement()) {
result << new AddBracesToIfOp(interface, index, ifStatement);
return;
}
}
template<typename ...Statements>
void checkControlStatements(const CppQuickFixInterface &interface, QuickFixOperations &result)
{
(... || checkControlStatementsHelper<Statements>(interface, result));
}
// ### This could very well be extended to the else branch
// and other nodes entirely.
void AddBracesToControlStatement::doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result)
{
if (interface.path().isEmpty())
return;
checkControlStatements<IfStatementAST,
WhileStatementAST,
ForStatementAST,
RangeBasedForStatementAST,
DoStatementAST>(interface, result);
}
namespace {
@@ -9969,7 +10012,7 @@ void createCppQuickFixes()
new SplitIfStatement;
new SplitSimpleDeclaration;
new AddBracesToIf;
new AddBracesToControlStatement;
new RearrangeParamDeclarationList;
new ReformatPointerDeclaration;

View File

@@ -287,7 +287,7 @@ public:
};
/*!
Add curly braces to a if statement that doesn't already contain a
Add curly braces to a control statement that doesn't already contain a
compound statement. I.e.
if (a)
@@ -297,9 +297,9 @@ public:
b;
}
Activates on: the if
Activates on: the keyword
*/
class AddBracesToIf: public CppQuickFixFactory
class AddBracesToControlStatement : public CppQuickFixFactory
{
public:
void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override;