forked from qt-creator/qt-creator
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:
@@ -98,6 +98,7 @@ add_qtc_plugin(CppEditor
|
||||
quickfixes/assigntolocalvariable.cpp quickfixes/assigntolocalvariable.h
|
||||
quickfixes/bringidentifierintoscope.cpp quickfixes/bringidentifierintoscope.h
|
||||
quickfixes/completeswitchstatement.cpp quickfixes/completeswitchstatement.h
|
||||
quickfixes/convertnumericliteral.cpp quickfixes/convertnumericliteral.h
|
||||
quickfixes/convertqt4connect.cpp quickfixes/convertqt4connect.h
|
||||
quickfixes/convertstringliteral.cpp quickfixes/convertstringliteral.h
|
||||
quickfixes/converttometamethodcall.cpp quickfixes/converttometamethodcall.h
|
||||
|
@@ -227,6 +227,8 @@ QtcPlugin {
|
||||
"completeswitchstatement.h",
|
||||
"convertfromandtopointer.cpp",
|
||||
"convertfromandtopointer.h",
|
||||
"convertnumericliteral.cpp",
|
||||
"convertnumericliteral.h",
|
||||
"convertqt4connect.cpp",
|
||||
"convertqt4connect.h",
|
||||
"convertstringliteral.cpp",
|
||||
|
205
src/plugins/cppeditor/quickfixes/convertnumericliteral.cpp
Normal file
205
src/plugins/cppeditor/quickfixes/convertnumericliteral.cpp
Normal 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
|
8
src/plugins/cppeditor/quickfixes/convertnumericliteral.h
Normal file
8
src/plugins/cppeditor/quickfixes/convertnumericliteral.h
Normal 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
|
@@ -16,6 +16,7 @@
|
||||
#include "bringidentifierintoscope.h"
|
||||
#include "completeswitchstatement.h"
|
||||
#include "convertfromandtopointer.h"
|
||||
#include "convertnumericliteral.h"
|
||||
#include "converttometamethodcall.h"
|
||||
#include "cppcodegenerationquickfixes.h"
|
||||
#include "cppinsertvirtualmethods.h"
|
||||
@@ -136,167 +137,6 @@ namespace 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;
|
||||
};
|
||||
|
||||
} // 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
|
||||
{
|
||||
public:
|
||||
@@ -638,8 +478,6 @@ void createCppQuickFixes()
|
||||
{
|
||||
new ConvertToCamelCase;
|
||||
|
||||
new ConvertNumericLiteral;
|
||||
|
||||
new RearrangeParamDeclarationList;
|
||||
new ReformatPointerDeclaration;
|
||||
|
||||
@@ -665,6 +503,7 @@ void createCppQuickFixes()
|
||||
registerCompleteSwitchStatementQuickfix();
|
||||
registerConvertToMetaMethodCallQuickfix();
|
||||
registerSplitSimpleDeclarationQuickfix();
|
||||
registerConvertNumericLiteralQuickfix();
|
||||
|
||||
new ExtraRefactoringOperations;
|
||||
}
|
||||
|
@@ -28,31 +28,6 @@ public:
|
||||
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
|
||||
"AN_EXAMPLE_SYMBOL" into "AnExampleSymbol".
|
||||
|
Reference in New Issue
Block a user