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/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
|
||||||
|
@@ -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",
|
||||||
|
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 "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;
|
||||||
}
|
}
|
||||||
|
@@ -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".
|
||||||
|
Reference in New Issue
Block a user