From a6758ab76c4d37abdb0fd0b392f8c1f134b4b294 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Thu, 16 May 2024 17:53:16 +0200 Subject: [PATCH] CppEditor: Move ConvertNumericLiteral quickfix to its own files Change-Id: Ic372fa9bab08ea294ca0890610696c0f06b8fecb Reviewed-by: Christian Stenger Reviewed-by: --- src/plugins/cppeditor/CMakeLists.txt | 1 + src/plugins/cppeditor/cppeditor.qbs | 2 + .../quickfixes/convertnumericliteral.cpp | 205 ++++++++++++++++++ .../quickfixes/convertnumericliteral.h | 8 + .../cppeditor/quickfixes/cppquickfixes.cpp | 165 +------------- .../cppeditor/quickfixes/cppquickfixes.h | 25 --- 6 files changed, 218 insertions(+), 188 deletions(-) create mode 100644 src/plugins/cppeditor/quickfixes/convertnumericliteral.cpp create mode 100644 src/plugins/cppeditor/quickfixes/convertnumericliteral.h diff --git a/src/plugins/cppeditor/CMakeLists.txt b/src/plugins/cppeditor/CMakeLists.txt index 11fc1297452..bc7d6d150c6 100644 --- a/src/plugins/cppeditor/CMakeLists.txt +++ b/src/plugins/cppeditor/CMakeLists.txt @@ -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 diff --git a/src/plugins/cppeditor/cppeditor.qbs b/src/plugins/cppeditor/cppeditor.qbs index 939a009d43f..9c0cd9ff3d9 100644 --- a/src/plugins/cppeditor/cppeditor.qbs +++ b/src/plugins/cppeditor/cppeditor.qbs @@ -227,6 +227,8 @@ QtcPlugin { "completeswitchstatement.h", "convertfromandtopointer.cpp", "convertfromandtopointer.h", + "convertnumericliteral.cpp", + "convertnumericliteral.h", "convertqt4connect.cpp", "convertqt4connect.h", "convertstringliteral.cpp", diff --git a/src/plugins/cppeditor/quickfixes/convertnumericliteral.cpp b/src/plugins/cppeditor/quickfixes/convertnumericliteral.cpp new file mode 100644 index 00000000000..5f62291e05b --- /dev/null +++ b/src/plugins/cppeditor/quickfixes/convertnumericliteral.cpp @@ -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 + +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 &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::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(); +} + +} // namespace CppEditor::Internal diff --git a/src/plugins/cppeditor/quickfixes/convertnumericliteral.h b/src/plugins/cppeditor/quickfixes/convertnumericliteral.h new file mode 100644 index 00000000000..4158807c689 --- /dev/null +++ b/src/plugins/cppeditor/quickfixes/convertnumericliteral.h @@ -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 diff --git a/src/plugins/cppeditor/quickfixes/cppquickfixes.cpp b/src/plugins/cppeditor/quickfixes/cppquickfixes.cpp index f6c12f06493..1432488ad90 100644 --- a/src/plugins/cppeditor/quickfixes/cppquickfixes.cpp +++ b/src/plugins/cppeditor/quickfixes/cppquickfixes.cpp @@ -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 &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::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; } diff --git a/src/plugins/cppeditor/quickfixes/cppquickfixes.h b/src/plugins/cppeditor/quickfixes/cppquickfixes.h index 2227d0a8a88..bb2312842e7 100644 --- a/src/plugins/cppeditor/quickfixes/cppquickfixes.h +++ b/src/plugins/cppeditor/quickfixes/cppquickfixes.h @@ -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".