CppEditor: Move ConvertNumericLiteral quickfix to its own files

Change-Id: Ic372fa9bab08ea294ca0890610696c0f06b8fecb
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
This commit is contained in:
Christian Kandeler
2024-05-16 17:53:16 +02:00
parent 072d9103c3
commit a6758ab76c
6 changed files with 218 additions and 188 deletions

View File

@@ -98,6 +98,7 @@ add_qtc_plugin(CppEditor
quickfixes/assigntolocalvariable.cpp quickfixes/assigntolocalvariable.h quickfixes/assigntolocalvariable.cpp quickfixes/assigntolocalvariable.h
quickfixes/bringidentifierintoscope.cpp quickfixes/bringidentifierintoscope.h quickfixes/bringidentifierintoscope.cpp quickfixes/bringidentifierintoscope.h
quickfixes/completeswitchstatement.cpp quickfixes/completeswitchstatement.h quickfixes/completeswitchstatement.cpp quickfixes/completeswitchstatement.h
quickfixes/convertnumericliteral.cpp quickfixes/convertnumericliteral.h
quickfixes/convertqt4connect.cpp quickfixes/convertqt4connect.h quickfixes/convertqt4connect.cpp quickfixes/convertqt4connect.h
quickfixes/convertstringliteral.cpp quickfixes/convertstringliteral.h quickfixes/convertstringliteral.cpp quickfixes/convertstringliteral.h
quickfixes/converttometamethodcall.cpp quickfixes/converttometamethodcall.h quickfixes/converttometamethodcall.cpp quickfixes/converttometamethodcall.h

View File

@@ -227,6 +227,8 @@ QtcPlugin {
"completeswitchstatement.h", "completeswitchstatement.h",
"convertfromandtopointer.cpp", "convertfromandtopointer.cpp",
"convertfromandtopointer.h", "convertfromandtopointer.h",
"convertnumericliteral.cpp",
"convertnumericliteral.h",
"convertqt4connect.cpp", "convertqt4connect.cpp",
"convertqt4connect.h", "convertqt4connect.h",
"convertstringliteral.cpp", "convertstringliteral.cpp",

View File

@@ -0,0 +1,205 @@
// 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 "convertnumericliteral.h"
#include "../cppeditortr.h"
#include "../cpprefactoringchanges.h"
#include "cppquickfix.h"
#include <bitset>
using namespace CPlusPlus;
using namespace Utils;
namespace CppEditor::Internal {
namespace {
class ConvertNumericLiteralOp: public CppQuickFixOperation
{
public:
ConvertNumericLiteralOp(const CppQuickFixInterface &interface, int start, int end,
const QString &replacement)
: CppQuickFixOperation(interface)
, start(start)
, end(end)
, replacement(replacement)
{}
void perform() override
{
CppRefactoringChanges refactoring(snapshot());
CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath());
ChangeSet changes;
changes.replace(start, end, replacement);
currentFile->setChangeSet(changes);
currentFile->apply();
}
private:
int start, end;
QString replacement;
};
/*!
Base class for converting numeric literals between decimal, octal and hex.
Does the base check for the specific ones and parses the number.
Test cases:
0xFA0Bu;
0X856A;
298.3;
199;
074;
199L;
074L;
-199;
-017;
0783; // invalid octal
0; // border case, allow only hex<->decimal
Activates on: numeric literals
*/
class ConvertNumericLiteral : 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();
CppRefactoringFilePtr file = interface.currentFile();
if (path.isEmpty())
return;
NumericLiteralAST *literal = path.last()->asNumericLiteral();
if (!literal)
return;
Token token = file->tokenAt(literal->asNumericLiteral()->literal_token);
if (!token.is(T_NUMERIC_LITERAL))
return;
const NumericLiteral *numeric = token.number;
if (numeric->isDouble() || numeric->isFloat())
return;
// remove trailing L or U and stuff
const char * const spell = numeric->chars();
int numberLength = numeric->size();
while (numberLength > 0 && !std::isxdigit(spell[numberLength - 1]))
--numberLength;
if (numberLength < 1)
return;
// convert to number
bool valid;
ulong value = 0;
const QString x = QString::fromUtf8(spell).left(numberLength);
if (x.startsWith("0b", Qt::CaseInsensitive))
value = x.mid(2).toULong(&valid, 2);
else
value = x.toULong(&valid, 0);
if (!valid)
return;
const int priority = path.size() - 1; // very high priority
const int start = file->startOf(literal);
const char * const str = numeric->chars();
const bool isBinary = numberLength > 2 && str[0] == '0' && (str[1] == 'b' || str[1] == 'B');
const bool isOctal = numberLength >= 2 && str[0] == '0' && str[1] >= '0' && str[1] <= '7';
const bool isDecimal = !(isBinary || isOctal || numeric->isHex());
if (!numeric->isHex()) {
/*
Convert integer literal to hex representation.
Replace
0b100000
32
040
With
0x20
*/
const QString replacement = QString::asprintf("0x%lX", value);
auto op = new ConvertNumericLiteralOp(interface, start, start + numberLength, replacement);
op->setDescription(Tr::tr("Convert to Hexadecimal"));
op->setPriority(priority);
result << op;
}
if (!isOctal) {
/*
Convert integer literal to octal representation.
Replace
0b100000
32
0x20
With
040
*/
const QString replacement = QString::asprintf("0%lo", value);
auto op = new ConvertNumericLiteralOp(interface, start, start + numberLength, replacement);
op->setDescription(Tr::tr("Convert to Octal"));
op->setPriority(priority);
result << op;
}
if (!isDecimal) {
/*
Convert integer literal to decimal representation.
Replace
0b100000
0x20
040
With
32
*/
const QString replacement = QString::asprintf("%lu", value);
auto op = new ConvertNumericLiteralOp(interface, start, start + numberLength, replacement);
op->setDescription(Tr::tr("Convert to Decimal"));
op->setPriority(priority);
result << op;
}
if (!isBinary) {
/*
Convert integer literal to binary representation.
Replace
32
0x20
040
With
0b100000
*/
QString replacement = "0b";
if (value == 0) {
replacement.append('0');
} else {
std::bitset<std::numeric_limits<decltype (value)>::digits> b(value);
QRegularExpression re("^[0]*");
replacement.append(QString::fromStdString(b.to_string()).remove(re));
}
auto op = new ConvertNumericLiteralOp(interface, start, start + numberLength, replacement);
op->setDescription(Tr::tr("Convert to Binary"));
op->setPriority(priority);
result << op;
}
}
};
} // namespace
void registerConvertNumericLiteralQuickfix()
{
CppQuickFixFactory::registerFactory<ConvertNumericLiteral>();
}
} // 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 registerConvertNumericLiteralQuickfix();
} // namespace CppEditor::Internal

View File

@@ -16,6 +16,7 @@
#include "bringidentifierintoscope.h" #include "bringidentifierintoscope.h"
#include "completeswitchstatement.h" #include "completeswitchstatement.h"
#include "convertfromandtopointer.h" #include "convertfromandtopointer.h"
#include "convertnumericliteral.h"
#include "converttometamethodcall.h" #include "converttometamethodcall.h"
#include "cppcodegenerationquickfixes.h" #include "cppcodegenerationquickfixes.h"
#include "cppinsertvirtualmethods.h" #include "cppinsertvirtualmethods.h"
@@ -136,167 +137,6 @@ namespace Internal {
namespace { namespace {
class ConvertNumericLiteralOp: public CppQuickFixOperation
{
public:
ConvertNumericLiteralOp(const CppQuickFixInterface &interface, int start, int end,
const QString &replacement)
: CppQuickFixOperation(interface)
, start(start)
, end(end)
, replacement(replacement)
{}
void perform() override
{
CppRefactoringChanges refactoring(snapshot());
CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath());
ChangeSet changes;
changes.replace(start, end, replacement);
currentFile->setChangeSet(changes);
currentFile->apply();
}
private:
int start, end;
QString replacement;
};
} // anonymous namespace
void ConvertNumericLiteral::doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result)
{
const QList<AST *> &path = interface.path();
CppRefactoringFilePtr file = interface.currentFile();
if (path.isEmpty())
return;
NumericLiteralAST *literal = path.last()->asNumericLiteral();
if (!literal)
return;
Token token = file->tokenAt(literal->asNumericLiteral()->literal_token);
if (!token.is(T_NUMERIC_LITERAL))
return;
const NumericLiteral *numeric = token.number;
if (numeric->isDouble() || numeric->isFloat())
return;
// remove trailing L or U and stuff
const char * const spell = numeric->chars();
int numberLength = numeric->size();
while (numberLength > 0 && !std::isxdigit(spell[numberLength - 1]))
--numberLength;
if (numberLength < 1)
return;
// convert to number
bool valid;
ulong value = 0;
const QString x = QString::fromUtf8(spell).left(numberLength);
if (x.startsWith("0b", Qt::CaseInsensitive))
value = x.mid(2).toULong(&valid, 2);
else
value = x.toULong(&valid, 0);
if (!valid)
return;
const int priority = path.size() - 1; // very high priority
const int start = file->startOf(literal);
const char * const str = numeric->chars();
const bool isBinary = numberLength > 2 && str[0] == '0' && (str[1] == 'b' || str[1] == 'B');
const bool isOctal = numberLength >= 2 && str[0] == '0' && str[1] >= '0' && str[1] <= '7';
const bool isDecimal = !(isBinary || isOctal || numeric->isHex());
if (!numeric->isHex()) {
/*
Convert integer literal to hex representation.
Replace
0b100000
32
040
With
0x20
*/
const QString replacement = QString::asprintf("0x%lX", value);
auto op = new ConvertNumericLiteralOp(interface, start, start + numberLength, replacement);
op->setDescription(Tr::tr("Convert to Hexadecimal"));
op->setPriority(priority);
result << op;
}
if (!isOctal) {
/*
Convert integer literal to octal representation.
Replace
0b100000
32
0x20
With
040
*/
const QString replacement = QString::asprintf("0%lo", value);
auto op = new ConvertNumericLiteralOp(interface, start, start + numberLength, replacement);
op->setDescription(Tr::tr("Convert to Octal"));
op->setPriority(priority);
result << op;
}
if (!isDecimal) {
/*
Convert integer literal to decimal representation.
Replace
0b100000
0x20
040
With
32
*/
const QString replacement = QString::asprintf("%lu", value);
auto op = new ConvertNumericLiteralOp(interface, start, start + numberLength, replacement);
op->setDescription(Tr::tr("Convert to Decimal"));
op->setPriority(priority);
result << op;
}
if (!isBinary) {
/*
Convert integer literal to binary representation.
Replace
32
0x20
040
With
0b100000
*/
QString replacement = "0b";
if (value == 0) {
replacement.append('0');
} else {
std::bitset<std::numeric_limits<decltype (value)>::digits> b(value);
QRegularExpression re("^[0]*");
replacement.append(QString::fromStdString(b.to_string()).remove(re));
}
auto op = new ConvertNumericLiteralOp(interface, start, start + numberLength, replacement);
op->setDescription(Tr::tr("Convert to Binary"));
op->setPriority(priority);
result << op;
}
}
namespace {
} // anonymous namespace
namespace {
class ConvertToCamelCaseOp: public CppQuickFixOperation class ConvertToCamelCaseOp: public CppQuickFixOperation
{ {
public: public:
@@ -638,8 +478,6 @@ void createCppQuickFixes()
{ {
new ConvertToCamelCase; new ConvertToCamelCase;
new ConvertNumericLiteral;
new RearrangeParamDeclarationList; new RearrangeParamDeclarationList;
new ReformatPointerDeclaration; new ReformatPointerDeclaration;
@@ -665,6 +503,7 @@ void createCppQuickFixes()
registerCompleteSwitchStatementQuickfix(); registerCompleteSwitchStatementQuickfix();
registerConvertToMetaMethodCallQuickfix(); registerConvertToMetaMethodCallQuickfix();
registerSplitSimpleDeclarationQuickfix(); registerSplitSimpleDeclarationQuickfix();
registerConvertNumericLiteralQuickfix();
new ExtraRefactoringOperations; new ExtraRefactoringOperations;
} }

View File

@@ -28,31 +28,6 @@ public:
void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override; void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override;
}; };
/*!
Base class for converting numeric literals between decimal, octal and hex.
Does the base check for the specific ones and parses the number.
Test cases:
0xFA0Bu;
0X856A;
298.3;
199;
074;
199L;
074L;
-199;
-017;
0783; // invalid octal
0; // border case, allow only hex<->decimal
Activates on: numeric literals
*/
class ConvertNumericLiteral: public CppQuickFixFactory
{
public:
void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override;
};
/*! /*!
Turns "an_example_symbol" into "anExampleSymbol" and Turns "an_example_symbol" into "anExampleSymbol" and
"AN_EXAMPLE_SYMBOL" into "AnExampleSymbol". "AN_EXAMPLE_SYMBOL" into "AnExampleSymbol".