aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/cppeditor/quickfixes/convertnumericliteral.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/cppeditor/quickfixes/convertnumericliteral.cpp')
-rw-r--r--src/plugins/cppeditor/quickfixes/convertnumericliteral.cpp199
1 files changed, 199 insertions, 0 deletions
diff --git a/src/plugins/cppeditor/quickfixes/convertnumericliteral.cpp b/src/plugins/cppeditor/quickfixes/convertnumericliteral.cpp
new file mode 100644
index 0000000000..6fb5becf34
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/convertnumericliteral.cpp
@@ -0,0 +1,199 @@
+// 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
+ {
+ currentFile()->apply(ChangeSet::makeReplace(start, end, replacement));
+ }
+
+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