Move quickfixes for logical operations into dedicated files

Change-Id: Ie0009820b7320ed71331e1611d6cf9701c54c089
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
Christian Kandeler
2024-05-16 10:57:17 +02:00
parent 44ab666928
commit 3c0258448e
6 changed files with 349 additions and 320 deletions

View File

@@ -111,6 +111,7 @@ add_qtc_plugin(CppEditor
quickfixes/cppquickfixsettingswidget.cpp quickfixes/cppquickfixsettingswidget.h quickfixes/cppquickfixsettingswidget.cpp quickfixes/cppquickfixsettingswidget.h
quickfixes/createdeclarationfromuse.cpp quickfixes/createdeclarationfromuse.h quickfixes/createdeclarationfromuse.cpp quickfixes/createdeclarationfromuse.h
quickfixes/insertfunctiondefinition.cpp quickfixes/insertfunctiondefinition.h quickfixes/insertfunctiondefinition.cpp quickfixes/insertfunctiondefinition.h
quickfixes/logicaloperationquickfixes.cpp quickfixes/logicaloperationquickfixes.h
quickfixes/moveclasstoownfile.cpp quickfixes/moveclasstoownfile.h quickfixes/moveclasstoownfile.cpp quickfixes/moveclasstoownfile.h
quickfixes/movefunctiondefinition.cpp quickfixes/movefunctiondefinition.h quickfixes/movefunctiondefinition.cpp quickfixes/movefunctiondefinition.h
quickfixes/removeusingnamespace.cpp quickfixes/removeusingnamespace.h quickfixes/removeusingnamespace.cpp quickfixes/removeusingnamespace.h

View File

@@ -251,6 +251,8 @@ QtcPlugin {
"createdeclarationfromuse.h", "createdeclarationfromuse.h",
"insertfunctiondefinition.cpp", "insertfunctiondefinition.cpp",
"insertfunctiondefinition.h", "insertfunctiondefinition.h",
"logicaloperationquickfixes.cpp",
"logicaloperationquickfixes.h",
"moveclasstoownfile.cpp", "moveclasstoownfile.cpp",
"moveclasstoownfile.h", "moveclasstoownfile.h",
"movefunctiondefinition.cpp", "movefunctiondefinition.cpp",

View File

@@ -5,7 +5,6 @@
#include "../baseeditordocumentprocessor.h" #include "../baseeditordocumentprocessor.h"
#include "../cppcodestylesettings.h" #include "../cppcodestylesettings.h"
#include "../cppeditordocument.h"
#include "../cppeditortr.h" #include "../cppeditortr.h"
#include "../cppeditorwidget.h" #include "../cppeditorwidget.h"
#include "../cppfunctiondecldeflink.h" #include "../cppfunctiondecldeflink.h"
@@ -25,6 +24,7 @@
#include "convertstringliteral.h" #include "convertstringliteral.h"
#include "createdeclarationfromuse.h" #include "createdeclarationfromuse.h"
#include "insertfunctiondefinition.h" #include "insertfunctiondefinition.h"
#include "logicaloperationquickfixes.h"
#include "moveclasstoownfile.h" #include "moveclasstoownfile.h"
#include "movefunctiondefinition.h" #include "movefunctiondefinition.h"
#include "removeusingnamespace.h" #include "removeusingnamespace.h"
@@ -37,6 +37,7 @@
#include <cplusplus/CppRewriter.h> #include <cplusplus/CppRewriter.h>
#include <cplusplus/declarationcomments.h> #include <cplusplus/declarationcomments.h>
#include <cplusplus/NamePrettyPrinter.h> #include <cplusplus/NamePrettyPrinter.h>
#include <cplusplus/Overview.h>
#include <cplusplus/TypeOfExpression.h> #include <cplusplus/TypeOfExpression.h>
#include <cplusplus/TypePrettyPrinter.h> #include <cplusplus/TypePrettyPrinter.h>
@@ -129,276 +130,6 @@ const QList<CppQuickFixFactory *> &CppQuickFixFactory::cppQuickFixFactories()
namespace Internal { namespace Internal {
namespace {
class InverseLogicalComparisonOp: public CppQuickFixOperation
{
public:
InverseLogicalComparisonOp(const CppQuickFixInterface &interface,
int priority,
BinaryExpressionAST *binary,
Kind invertToken)
: CppQuickFixOperation(interface, priority)
, binary(binary)
{
Token tok;
tok.f.kind = invertToken;
replacement = QLatin1String(tok.spell());
// check for enclosing nested expression
if (priority - 1 >= 0)
nested = interface.path()[priority - 1]->asNestedExpression();
// check for ! before parentheses
if (nested && priority - 2 >= 0) {
negation = interface.path()[priority - 2]->asUnaryExpression();
if (negation && !interface.currentFile()->tokenAt(negation->unary_op_token).is(T_EXCLAIM))
negation = nullptr;
}
}
QString description() const override
{
return Tr::tr("Rewrite Using %1").arg(replacement);
}
void perform() override
{
CppRefactoringChanges refactoring(snapshot());
CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath());
ChangeSet changes;
if (negation) {
// can't remove parentheses since that might break precedence
changes.remove(currentFile->range(negation->unary_op_token));
} else if (nested) {
changes.insert(currentFile->startOf(nested), QLatin1String("!"));
} else {
changes.insert(currentFile->startOf(binary), QLatin1String("!("));
changes.insert(currentFile->endOf(binary), QLatin1String(")"));
}
changes.replace(currentFile->range(binary->binary_op_token), replacement);
currentFile->setChangeSet(changes);
currentFile->apply();
}
private:
BinaryExpressionAST *binary = nullptr;
NestedExpressionAST *nested = nullptr;
UnaryExpressionAST *negation = nullptr;
QString replacement;
};
} // anonymous namespace
void InverseLogicalComparison::doMatch(const CppQuickFixInterface &interface,
QuickFixOperations &result)
{
CppRefactoringFilePtr file = interface.currentFile();
const QList<AST *> &path = interface.path();
if (path.isEmpty())
return;
int index = path.size() - 1;
BinaryExpressionAST *binary = path.at(index)->asBinaryExpression();
if (!binary)
return;
if (!interface.isCursorOn(binary->binary_op_token))
return;
Kind invertToken;
switch (file->tokenAt(binary->binary_op_token).kind()) {
case T_LESS_EQUAL:
invertToken = T_GREATER;
break;
case T_LESS:
invertToken = T_GREATER_EQUAL;
break;
case T_GREATER:
invertToken = T_LESS_EQUAL;
break;
case T_GREATER_EQUAL:
invertToken = T_LESS;
break;
case T_EQUAL_EQUAL:
invertToken = T_EXCLAIM_EQUAL;
break;
case T_EXCLAIM_EQUAL:
invertToken = T_EQUAL_EQUAL;
break;
default:
return;
}
result << new InverseLogicalComparisonOp(interface, index, binary, invertToken);
}
namespace {
class FlipLogicalOperandsOp: public CppQuickFixOperation
{
public:
FlipLogicalOperandsOp(const CppQuickFixInterface &interface, int priority,
BinaryExpressionAST *binary, QString replacement)
: CppQuickFixOperation(interface)
, binary(binary)
, replacement(replacement)
{
setPriority(priority);
}
QString description() const override
{
if (replacement.isEmpty())
return Tr::tr("Swap Operands");
else
return Tr::tr("Rewrite Using %1").arg(replacement);
}
void perform() override
{
CppRefactoringChanges refactoring(snapshot());
CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath());
ChangeSet changes;
changes.flip(currentFile->range(binary->left_expression),
currentFile->range(binary->right_expression));
if (!replacement.isEmpty())
changes.replace(currentFile->range(binary->binary_op_token), replacement);
currentFile->setChangeSet(changes);
currentFile->apply();
}
private:
BinaryExpressionAST *binary;
QString replacement;
};
} // anonymous namespace
void FlipLogicalOperands::doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result)
{
const QList<AST *> &path = interface.path();
if (path.isEmpty())
return;
CppRefactoringFilePtr file = interface.currentFile();
int index = path.size() - 1;
BinaryExpressionAST *binary = path.at(index)->asBinaryExpression();
if (!binary)
return;
if (!interface.isCursorOn(binary->binary_op_token))
return;
Kind flipToken;
switch (file->tokenAt(binary->binary_op_token).kind()) {
case T_LESS_EQUAL:
flipToken = T_GREATER_EQUAL;
break;
case T_LESS:
flipToken = T_GREATER;
break;
case T_GREATER:
flipToken = T_LESS;
break;
case T_GREATER_EQUAL:
flipToken = T_LESS_EQUAL;
break;
case T_EQUAL_EQUAL:
case T_EXCLAIM_EQUAL:
case T_AMPER_AMPER:
case T_PIPE_PIPE:
flipToken = T_EOF_SYMBOL;
break;
default:
return;
}
QString replacement;
if (flipToken != T_EOF_SYMBOL) {
Token tok;
tok.f.kind = flipToken;
replacement = QLatin1String(tok.spell());
}
result << new FlipLogicalOperandsOp(interface, index, binary, replacement);
}
namespace {
class RewriteLogicalAndOp: public CppQuickFixOperation
{
public:
std::shared_ptr<ASTPatternBuilder> mk;
UnaryExpressionAST *left;
UnaryExpressionAST *right;
BinaryExpressionAST *pattern;
RewriteLogicalAndOp(const CppQuickFixInterface &interface)
: CppQuickFixOperation(interface)
, mk(new ASTPatternBuilder)
{
left = mk->UnaryExpression();
right = mk->UnaryExpression();
pattern = mk->BinaryExpression(left, right);
}
void perform() override
{
CppRefactoringChanges refactoring(snapshot());
CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath());
ChangeSet changes;
changes.replace(currentFile->range(pattern->binary_op_token), QLatin1String("||"));
changes.remove(currentFile->range(left->unary_op_token));
changes.remove(currentFile->range(right->unary_op_token));
const int start = currentFile->startOf(pattern);
const int end = currentFile->endOf(pattern);
changes.insert(start, QLatin1String("!("));
changes.insert(end, QLatin1String(")"));
currentFile->setChangeSet(changes);
currentFile->apply();
}
};
} // anonymous namespace
void RewriteLogicalAnd::doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result)
{
BinaryExpressionAST *expression = nullptr;
const QList<AST *> &path = interface.path();
CppRefactoringFilePtr file = interface.currentFile();
int index = path.size() - 1;
for (; index != -1; --index) {
expression = path.at(index)->asBinaryExpression();
if (expression)
break;
}
if (!expression)
return;
if (!interface.isCursorOn(expression->binary_op_token))
return;
QSharedPointer<RewriteLogicalAndOp> op(new RewriteLogicalAndOp(interface));
ASTMatcher matcher;
if (expression->match(op->pattern, &matcher) &&
file->tokenAt(op->pattern->binary_op_token).is(T_AMPER_AMPER) &&
file->tokenAt(op->left->unary_op_token).is(T_EXCLAIM) &&
file->tokenAt(op->right->unary_op_token).is(T_EXCLAIM)) {
op->setDescription(Tr::tr("Rewrite Condition Using ||"));
op->setPriority(index);
result.append(op);
}
}
static bool checkDeclarationForSplit(SimpleDeclarationAST *declaration) static bool checkDeclarationForSplit(SimpleDeclarationAST *declaration)
{ {
if (!declaration->semicolon_token) if (!declaration->semicolon_token)
@@ -3834,10 +3565,6 @@ void ConvertToMetaMethodCall::doMatch(const CppQuickFixInterface &interface,
void createCppQuickFixes() void createCppQuickFixes()
{ {
new FlipLogicalOperands;
new InverseLogicalComparison;
new RewriteLogicalAnd;
new ConvertToCamelCase; new ConvertToCamelCase;
new ConvertNumericLiteral; new ConvertNumericLiteral;
@@ -3871,6 +3598,7 @@ void createCppQuickFixes()
registerBringIdentifierIntoScopeQuickfixes(); registerBringIdentifierIntoScopeQuickfixes();
registerConvertStringLiteralQuickfixes(); registerConvertStringLiteralQuickfixes();
registerCreateDeclarationFromUseQuickfixes(); registerCreateDeclarationFromUseQuickfixes();
registerLogicalOperationQuickfixes();
new OptimizeForLoop; new OptimizeForLoop;

View File

@@ -28,51 +28,6 @@ public:
void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override; void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override;
}; };
/*!
Rewrite
a op b
As
b flipop a
Activates on: <= < > >= == != && ||
*/
class FlipLogicalOperands: public CppQuickFixFactory
{
public:
void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override;
};
/*!
Rewrite
a op b -> !(a invop b)
(a op b) -> !(a invop b)
!(a op b) -> (a invob b)
Activates on: <= < > >= == !=
*/
class InverseLogicalComparison: public CppQuickFixFactory
{
public:
void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override;
};
/*!
Rewrite
!a && !b
As
!(a || b)
Activates on: &&
*/
class RewriteLogicalAnd: public CppQuickFixFactory
{
public:
void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override;
};
/*! /*!
Base class for converting numeric literals between decimal, octal and hex. Base class for converting numeric literals between decimal, octal and hex.
Does the base check for the specific ones and parses the number. Does the base check for the specific ones and parses the number.

View File

@@ -0,0 +1,335 @@
// 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 "logicaloperationquickfixes.h"
#include "../cppeditortr.h"
#include "../cpprefactoringchanges.h"
#include "cppquickfix.h"
using namespace CPlusPlus;
using namespace Utils;
namespace CppEditor::Internal {
namespace {
class FlipLogicalOperandsOp : public CppQuickFixOperation
{
public:
FlipLogicalOperandsOp(const CppQuickFixInterface &interface, int priority,
BinaryExpressionAST *binary, QString replacement)
: CppQuickFixOperation(interface)
, binary(binary)
, replacement(replacement)
{
setPriority(priority);
}
QString description() const override
{
if (replacement.isEmpty())
return Tr::tr("Swap Operands");
else
return Tr::tr("Rewrite Using %1").arg(replacement);
}
void perform() override
{
CppRefactoringChanges refactoring(snapshot());
CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath());
ChangeSet changes;
changes.flip(currentFile->range(binary->left_expression),
currentFile->range(binary->right_expression));
if (!replacement.isEmpty())
changes.replace(currentFile->range(binary->binary_op_token), replacement);
currentFile->setChangeSet(changes);
currentFile->apply();
}
private:
BinaryExpressionAST *binary;
QString replacement;
};
class InverseLogicalComparisonOp : public CppQuickFixOperation
{
public:
InverseLogicalComparisonOp(const CppQuickFixInterface &interface,
int priority,
BinaryExpressionAST *binary,
Kind invertToken)
: CppQuickFixOperation(interface, priority)
, binary(binary)
{
Token tok;
tok.f.kind = invertToken;
replacement = QLatin1String(tok.spell());
// check for enclosing nested expression
if (priority - 1 >= 0)
nested = interface.path()[priority - 1]->asNestedExpression();
// check for ! before parentheses
if (nested && priority - 2 >= 0) {
negation = interface.path()[priority - 2]->asUnaryExpression();
if (negation && !interface.currentFile()->tokenAt(negation->unary_op_token).is(T_EXCLAIM))
negation = nullptr;
}
}
QString description() const override
{
return Tr::tr("Rewrite Using %1").arg(replacement);
}
void perform() override
{
CppRefactoringChanges refactoring(snapshot());
CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath());
ChangeSet changes;
if (negation) {
// can't remove parentheses since that might break precedence
changes.remove(currentFile->range(negation->unary_op_token));
} else if (nested) {
changes.insert(currentFile->startOf(nested), QLatin1String("!"));
} else {
changes.insert(currentFile->startOf(binary), QLatin1String("!("));
changes.insert(currentFile->endOf(binary), QLatin1String(")"));
}
changes.replace(currentFile->range(binary->binary_op_token), replacement);
currentFile->setChangeSet(changes);
currentFile->apply();
}
private:
BinaryExpressionAST *binary = nullptr;
NestedExpressionAST *nested = nullptr;
UnaryExpressionAST *negation = nullptr;
QString replacement;
};
class RewriteLogicalAndOp : public CppQuickFixOperation
{
public:
std::shared_ptr<ASTPatternBuilder> mk;
UnaryExpressionAST *left;
UnaryExpressionAST *right;
BinaryExpressionAST *pattern;
RewriteLogicalAndOp(const CppQuickFixInterface &interface)
: CppQuickFixOperation(interface)
, mk(new ASTPatternBuilder)
{
left = mk->UnaryExpression();
right = mk->UnaryExpression();
pattern = mk->BinaryExpression(left, right);
}
void perform() override
{
CppRefactoringChanges refactoring(snapshot());
CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath());
ChangeSet changes;
changes.replace(currentFile->range(pattern->binary_op_token), QLatin1String("||"));
changes.remove(currentFile->range(left->unary_op_token));
changes.remove(currentFile->range(right->unary_op_token));
const int start = currentFile->startOf(pattern);
const int end = currentFile->endOf(pattern);
changes.insert(start, QLatin1String("!("));
changes.insert(end, QLatin1String(")"));
currentFile->setChangeSet(changes);
currentFile->apply();
}
};
/*!
Rewrite
a op b
As
b flipop a
Activates on: <= < > >= == != && ||
*/
class FlipLogicalOperands : public CppQuickFixFactory
{
#ifdef WITH_TESTS
public:
static QObject *createTest() { return new QObject; }
#endif
private:
void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
{
const QList<AST *> &path = interface.path();
if (path.isEmpty())
return;
CppRefactoringFilePtr file = interface.currentFile();
int index = path.size() - 1;
BinaryExpressionAST *binary = path.at(index)->asBinaryExpression();
if (!binary)
return;
if (!interface.isCursorOn(binary->binary_op_token))
return;
Kind flipToken;
switch (file->tokenAt(binary->binary_op_token).kind()) {
case T_LESS_EQUAL:
flipToken = T_GREATER_EQUAL;
break;
case T_LESS:
flipToken = T_GREATER;
break;
case T_GREATER:
flipToken = T_LESS;
break;
case T_GREATER_EQUAL:
flipToken = T_LESS_EQUAL;
break;
case T_EQUAL_EQUAL:
case T_EXCLAIM_EQUAL:
case T_AMPER_AMPER:
case T_PIPE_PIPE:
flipToken = T_EOF_SYMBOL;
break;
default:
return;
}
QString replacement;
if (flipToken != T_EOF_SYMBOL) {
Token tok;
tok.f.kind = flipToken;
replacement = QLatin1String(tok.spell());
}
result << new FlipLogicalOperandsOp(interface, index, binary, replacement);
}
};
/*!
Rewrite
a op b -> !(a invop b)
(a op b) -> !(a invop b)
!(a op b) -> (a invob b)
Activates on: <= < > >= == !=
*/
class InverseLogicalComparison : public CppQuickFixFactory
{
#ifdef WITH_TESTS
public:
static QObject *createTest() { return new QObject; }
#endif
private:
void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
{
CppRefactoringFilePtr file = interface.currentFile();
const QList<AST *> &path = interface.path();
if (path.isEmpty())
return;
int index = path.size() - 1;
BinaryExpressionAST *binary = path.at(index)->asBinaryExpression();
if (!binary)
return;
if (!interface.isCursorOn(binary->binary_op_token))
return;
Kind invertToken;
switch (file->tokenAt(binary->binary_op_token).kind()) {
case T_LESS_EQUAL:
invertToken = T_GREATER;
break;
case T_LESS:
invertToken = T_GREATER_EQUAL;
break;
case T_GREATER:
invertToken = T_LESS_EQUAL;
break;
case T_GREATER_EQUAL:
invertToken = T_LESS;
break;
case T_EQUAL_EQUAL:
invertToken = T_EXCLAIM_EQUAL;
break;
case T_EXCLAIM_EQUAL:
invertToken = T_EQUAL_EQUAL;
break;
default:
return;
}
result << new InverseLogicalComparisonOp(interface, index, binary, invertToken);
}
};
/*!
Rewrite
!a && !b
As
!(a || b)
Activates on: &&
*/
class RewriteLogicalAnd : public CppQuickFixFactory
{
#ifdef WITH_TESTS
public:
static QObject *createTest() { return new QObject; }
#endif
private:
void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
{
BinaryExpressionAST *expression = nullptr;
const QList<AST *> &path = interface.path();
CppRefactoringFilePtr file = interface.currentFile();
int index = path.size() - 1;
for (; index != -1; --index) {
expression = path.at(index)->asBinaryExpression();
if (expression)
break;
}
if (!expression)
return;
if (!interface.isCursorOn(expression->binary_op_token))
return;
QSharedPointer<RewriteLogicalAndOp> op(new RewriteLogicalAndOp(interface));
ASTMatcher matcher;
if (expression->match(op->pattern, &matcher) &&
file->tokenAt(op->pattern->binary_op_token).is(T_AMPER_AMPER) &&
file->tokenAt(op->left->unary_op_token).is(T_EXCLAIM) &&
file->tokenAt(op->right->unary_op_token).is(T_EXCLAIM)) {
op->setDescription(Tr::tr("Rewrite Condition Using ||"));
op->setPriority(index);
result.append(op);
}
}
};
} // namespace
void registerLogicalOperationQuickfixes()
{
CppQuickFixFactory::registerFactory<FlipLogicalOperands>();
CppQuickFixFactory::registerFactory<InverseLogicalComparison>();
CppQuickFixFactory::registerFactory<RewriteLogicalAnd>();
}
} // namespace CppEditor::Internal

View File

@@ -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 registerLogicalOperationQuickfixes();
} // namespace CppEditor::Internal