Move ConvertFromAndToPointer quickfix to its own files

Change-Id: Ie5f80072e5225ee0394ad0dc1dde2309133aae9d
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 15:58:56 +02:00
parent a82ad37bde
commit f0695dacf9
7 changed files with 718 additions and 672 deletions

View File

@@ -109,6 +109,7 @@ add_qtc_plugin(CppEditor
quickfixes/cppquickfixsettings.cpp quickfixes/cppquickfixsettings.h quickfixes/cppquickfixsettings.cpp quickfixes/cppquickfixsettings.h
quickfixes/cppquickfixsettingspage.cpp quickfixes/cppquickfixsettingspage.h quickfixes/cppquickfixsettingspage.cpp quickfixes/cppquickfixsettingspage.h
quickfixes/cppquickfixsettingswidget.cpp quickfixes/cppquickfixsettingswidget.h quickfixes/cppquickfixsettingswidget.cpp quickfixes/cppquickfixsettingswidget.h
quickfixes/convertfromandtopointer.cpp quickfixes/convertfromandtopointer.h
quickfixes/createdeclarationfromuse.cpp quickfixes/createdeclarationfromuse.h quickfixes/createdeclarationfromuse.cpp quickfixes/createdeclarationfromuse.h
quickfixes/extractfunction.cpp quickfixes/extractfunction.h quickfixes/extractfunction.cpp quickfixes/extractfunction.h
quickfixes/extractliteralasparameter.cpp quickfixes/extractliteralasparameter.h quickfixes/extractliteralasparameter.cpp quickfixes/extractliteralasparameter.h

View File

@@ -221,6 +221,8 @@ QtcPlugin {
files: [ files: [
"bringidentifierintoscope.cpp", "bringidentifierintoscope.cpp",
"bringidentifierintoscope.h", "bringidentifierintoscope.h",
"convertfromandtopointer.cpp",
"convertfromandtopointer.h",
"convertqt4connect.cpp", "convertqt4connect.cpp",
"convertqt4connect.h", "convertqt4connect.h",
"convertstringliteral.cpp", "convertstringliteral.cpp",

View File

@@ -0,0 +1,705 @@
// 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 "convertfromandtopointer.h"
#include "../cppeditortr.h"
#include "../cpprefactoringchanges.h"
#include "cppquickfix.h"
#include <cplusplus/ASTPath.h>
#include <cplusplus/Overview.h>
#include <cplusplus/TypeOfExpression.h>
#ifdef WITH_TESTS
#include "cppquickfix_test.h"
#include <QtTest>
#endif
using namespace CPlusPlus;
using namespace Utils;
namespace CppEditor::Internal {
namespace {
class ConvertFromAndToPointerOp : public CppQuickFixOperation
{
public:
enum Mode { FromPointer, FromVariable, FromReference };
ConvertFromAndToPointerOp(const CppQuickFixInterface &interface, int priority, Mode mode,
bool isAutoDeclaration,
const SimpleDeclarationAST *simpleDeclaration,
const DeclaratorAST *declaratorAST,
const SimpleNameAST *identifierAST,
Symbol *symbol)
: CppQuickFixOperation(interface, priority)
, m_mode(mode)
, m_isAutoDeclaration(isAutoDeclaration)
, m_simpleDeclaration(simpleDeclaration)
, m_declaratorAST(declaratorAST)
, m_identifierAST(identifierAST)
, m_symbol(symbol)
, m_refactoring(snapshot())
, m_file(m_refactoring.cppFile(filePath()))
, m_document(interface.semanticInfo().doc)
{
setDescription(
mode == FromPointer
? Tr::tr("Convert to Stack Variable")
: Tr::tr("Convert to Pointer"));
}
void perform() override
{
ChangeSet changes;
switch (m_mode) {
case FromPointer:
removePointerOperator(changes);
convertToStackVariable(changes);
break;
case FromReference:
removeReferenceOperator(changes);
Q_FALLTHROUGH();
case FromVariable:
convertToPointer(changes);
break;
}
m_file->setChangeSet(changes);
m_file->apply();
}
private:
void removePointerOperator(ChangeSet &changes) const
{
if (!m_declaratorAST->ptr_operator_list)
return;
PointerAST *ptrAST = m_declaratorAST->ptr_operator_list->value->asPointer();
QTC_ASSERT(ptrAST, return);
const int pos = m_file->startOf(ptrAST->star_token);
changes.remove(pos, pos + 1);
}
void removeReferenceOperator(ChangeSet &changes) const
{
ReferenceAST *refAST = m_declaratorAST->ptr_operator_list->value->asReference();
QTC_ASSERT(refAST, return);
const int pos = m_file->startOf(refAST->reference_token);
changes.remove(pos, pos + 1);
}
void removeNewExpression(ChangeSet &changes, NewExpressionAST *newExprAST) const
{
ExpressionListAST *exprlist = nullptr;
if (newExprAST->new_initializer) {
if (ExpressionListParenAST *ast = newExprAST->new_initializer->asExpressionListParen())
exprlist = ast->expression_list;
else if (BracedInitializerAST *ast = newExprAST->new_initializer->asBracedInitializer())
exprlist = ast->expression_list;
}
if (exprlist) {
// remove 'new' keyword and type before initializer
changes.remove(m_file->startOf(newExprAST->new_token),
m_file->startOf(newExprAST->new_initializer));
changes.remove(m_file->endOf(m_declaratorAST->equal_token - 1),
m_file->startOf(m_declaratorAST->equal_token + 1));
} else {
// remove the whole new expression
changes.remove(m_file->endOf(m_identifierAST->firstToken()),
m_file->startOf(newExprAST->lastToken()));
}
}
void removeNewKeyword(ChangeSet &changes, NewExpressionAST *newExprAST) const
{
// remove 'new' keyword before initializer
changes.remove(m_file->startOf(newExprAST->new_token),
m_file->startOf(newExprAST->new_type_id));
}
void convertToStackVariable(ChangeSet &changes) const
{
// Handle the initializer.
if (m_declaratorAST->initializer) {
if (NewExpressionAST *newExpression = m_declaratorAST->initializer->asNewExpression()) {
if (m_isAutoDeclaration) {
if (!newExpression->new_initializer)
changes.insert(m_file->endOf(newExpression), QStringLiteral("()"));
removeNewKeyword(changes, newExpression);
} else {
removeNewExpression(changes, newExpression);
}
}
}
// Fix all occurrences of the identifier in this function.
ASTPath astPath(m_document);
const QList<SemanticInfo::Use> uses = semanticInfo().localUses.value(m_symbol);
for (const SemanticInfo::Use &use : uses) {
const QList<AST *> path = astPath(use.line, use.column);
AST *idAST = path.last();
bool declarationFound = false;
bool starFound = false;
int ampersandPos = 0;
bool memberAccess = false;
bool deleteCall = false;
for (int i = path.count() - 2; i >= 0; --i) {
if (path.at(i) == m_declaratorAST) {
declarationFound = true;
break;
}
if (MemberAccessAST *memberAccessAST = path.at(i)->asMemberAccess()) {
if (m_file->tokenAt(memberAccessAST->access_token).kind() != T_ARROW)
continue;
int pos = m_file->startOf(memberAccessAST->access_token);
changes.replace(pos, pos + 2, QLatin1String("."));
memberAccess = true;
break;
} else if (DeleteExpressionAST *deleteAST = path.at(i)->asDeleteExpression()) {
const int pos = m_file->startOf(deleteAST->delete_token);
changes.insert(pos, QLatin1String("// "));
deleteCall = true;
break;
} else if (UnaryExpressionAST *unaryExprAST = path.at(i)->asUnaryExpression()) {
const Token tk = m_file->tokenAt(unaryExprAST->unary_op_token);
if (tk.kind() == T_STAR) {
if (!starFound) {
int pos = m_file->startOf(unaryExprAST->unary_op_token);
changes.remove(pos, pos + 1);
}
starFound = true;
} else if (tk.kind() == T_AMPER) {
ampersandPos = m_file->startOf(unaryExprAST->unary_op_token);
}
} else if (PointerAST *ptrAST = path.at(i)->asPointer()) {
if (!starFound) {
const int pos = m_file->startOf(ptrAST->star_token);
changes.remove(pos, pos);
}
starFound = true;
} else if (path.at(i)->asFunctionDefinition()) {
break;
}
}
if (!declarationFound && !starFound && !memberAccess && !deleteCall) {
if (ampersandPos) {
changes.insert(ampersandPos, QLatin1String("&("));
changes.insert(m_file->endOf(idAST->firstToken()), QLatin1String(")"));
} else {
changes.insert(m_file->startOf(idAST), QLatin1String("&"));
}
}
}
}
QString typeNameOfDeclaration() const
{
if (!m_simpleDeclaration
|| !m_simpleDeclaration->decl_specifier_list
|| !m_simpleDeclaration->decl_specifier_list->value) {
return QString();
}
NamedTypeSpecifierAST *namedType
= m_simpleDeclaration->decl_specifier_list->value->asNamedTypeSpecifier();
if (!namedType)
return QString();
Overview overview;
return overview.prettyName(namedType->name->name);
}
void insertNewExpression(ChangeSet &changes, ExpressionAST *ast) const
{
const QString typeName = typeNameOfDeclaration();
if (CallAST *callAST = ast->asCall()) {
if (typeName.isEmpty()) {
changes.insert(m_file->startOf(callAST), QLatin1String("new "));
} else {
changes.insert(m_file->startOf(callAST),
QLatin1String("new ") + typeName + QLatin1Char('('));
changes.insert(m_file->startOf(callAST->lastToken()), QLatin1String(")"));
}
} else {
if (typeName.isEmpty())
return;
changes.insert(m_file->startOf(ast), QLatin1String(" = new ") + typeName);
}
}
void insertNewExpression(ChangeSet &changes) const
{
const QString typeName = typeNameOfDeclaration();
if (typeName.isEmpty())
return;
changes.insert(m_file->endOf(m_identifierAST->firstToken()),
QLatin1String(" = new ") + typeName);
}
void convertToPointer(ChangeSet &changes) const
{
// Handle initializer.
if (m_declaratorAST->initializer) {
if (IdExpressionAST *idExprAST = m_declaratorAST->initializer->asIdExpression()) {
changes.insert(m_file->startOf(idExprAST), QLatin1String("&"));
} else if (CallAST *callAST = m_declaratorAST->initializer->asCall()) {
insertNewExpression(changes, callAST);
} else if (ExpressionListParenAST *exprListAST = m_declaratorAST->initializer
->asExpressionListParen()) {
insertNewExpression(changes, exprListAST);
} else if (BracedInitializerAST *bracedInitializerAST = m_declaratorAST->initializer
->asBracedInitializer()) {
insertNewExpression(changes, bracedInitializerAST);
}
} else {
insertNewExpression(changes);
}
// Fix all occurrences of the identifier in this function.
ASTPath astPath(m_document);
const QList<SemanticInfo::Use> uses = semanticInfo().localUses.value(m_symbol);
for (const SemanticInfo::Use &use : uses) {
const QList<AST *> path = astPath(use.line, use.column);
AST *idAST = path.last();
bool insertStar = true;
for (int i = path.count() - 2; i >= 0; --i) {
if (m_isAutoDeclaration && path.at(i) == m_declaratorAST) {
insertStar = false;
break;
}
if (MemberAccessAST *memberAccessAST = path.at(i)->asMemberAccess()) {
const int pos = m_file->startOf(memberAccessAST->access_token);
changes.replace(pos, pos + 1, QLatin1String("->"));
insertStar = false;
break;
} else if (UnaryExpressionAST *unaryExprAST = path.at(i)->asUnaryExpression()) {
if (m_file->tokenAt(unaryExprAST->unary_op_token).kind() == T_AMPER) {
const int pos = m_file->startOf(unaryExprAST->unary_op_token);
changes.remove(pos, pos + 1);
insertStar = false;
break;
}
} else if (path.at(i)->asFunctionDefinition()) {
break;
}
}
if (insertStar)
changes.insert(m_file->startOf(idAST), QLatin1String("*"));
}
}
const Mode m_mode;
const bool m_isAutoDeclaration;
const SimpleDeclarationAST * const m_simpleDeclaration;
const DeclaratorAST * const m_declaratorAST;
const SimpleNameAST * const m_identifierAST;
Symbol * const m_symbol;
const CppRefactoringChanges m_refactoring;
const CppRefactoringFilePtr m_file;
const Document::Ptr m_document;
};
/*!
Converts the selected variable to a pointer if it is a stack variable or reference, or vice versa.
Activates on variable declarations.
*/
class ConvertFromAndToPointer : public CppQuickFixFactory
{
#ifdef WITH_TESTS
public:
static QObject *createTest();
#endif
private:
void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
{
const QList<AST *> &path = interface.path();
if (path.count() < 2)
return;
SimpleNameAST *identifier = path.last()->asSimpleName();
if (!identifier)
return;
SimpleDeclarationAST *simpleDeclaration = nullptr;
DeclaratorAST *declarator = nullptr;
bool isFunctionLocal = false;
bool isClassLocal = false;
ConvertFromAndToPointerOp::Mode mode = ConvertFromAndToPointerOp::FromVariable;
for (int i = path.count() - 2; i >= 0; --i) {
AST *ast = path.at(i);
if (!declarator && (declarator = ast->asDeclarator()))
continue;
if (!simpleDeclaration && (simpleDeclaration = ast->asSimpleDeclaration()))
continue;
if (declarator && simpleDeclaration) {
if (ast->asClassSpecifier()) {
isClassLocal = true;
} else if (ast->asFunctionDefinition() && !isClassLocal) {
isFunctionLocal = true;
break;
}
}
}
if (!isFunctionLocal || !simpleDeclaration || !declarator)
return;
Symbol *symbol = nullptr;
for (List<Symbol *> *lst = simpleDeclaration->symbols; lst; lst = lst->next) {
if (lst->value->name() == identifier->name) {
symbol = lst->value;
break;
}
}
if (!symbol)
return;
bool isAutoDeclaration = false;
if (symbol->storage() == Symbol::Auto) {
// For auto variables we must deduce the type from the initializer.
if (!declarator->initializer)
return;
isAutoDeclaration = true;
TypeOfExpression typeOfExpression;
typeOfExpression.init(interface.semanticInfo().doc, interface.snapshot());
typeOfExpression.setExpandTemplates(true);
CppRefactoringFilePtr file = interface.currentFile();
Scope *scope = file->scopeAt(declarator->firstToken());
QList<LookupItem> result = typeOfExpression(file->textOf(declarator->initializer).toUtf8(),
scope, TypeOfExpression::Preprocess);
if (!result.isEmpty() && result.first().type()->asPointerType())
mode = ConvertFromAndToPointerOp::FromPointer;
} else if (declarator->ptr_operator_list) {
for (PtrOperatorListAST *ops = declarator->ptr_operator_list; ops; ops = ops->next) {
if (ops != declarator->ptr_operator_list) {
// Bail out on more complex pointer types (e.g. pointer of pointer,
// or reference of pointer).
return;
}
if (ops->value->asPointer())
mode = ConvertFromAndToPointerOp::FromPointer;
else if (ops->value->asReference())
mode = ConvertFromAndToPointerOp::FromReference;
}
}
const int priority = path.size() - 1;
result << new ConvertFromAndToPointerOp(interface, priority, mode, isAutoDeclaration,
simpleDeclaration, declarator, identifier, symbol);
}
};
#ifdef WITH_TESTS
using namespace Tests;
class ConvertFromAndToPointerTest : public QObject
{
Q_OBJECT
private slots:
void test_data()
{
QTest::addColumn<QByteArray>("original");
QTest::addColumn<QByteArray>("expected");
QTest::newRow("ConvertFromPointer")
<< QByteArray("void foo() {\n"
" QString *@str;\n"
" if (!str->isEmpty())\n"
" str->clear();\n"
" f1(*str);\n"
" f2(str);\n"
"}\n")
<< QByteArray("void foo() {\n"
" QString str;\n"
" if (!str.isEmpty())\n"
" str.clear();\n"
" f1(str);\n"
" f2(&str);\n"
"}\n");
QTest::newRow("ConvertToPointer")
<< QByteArray("void foo() {\n"
" QString @str;\n"
" if (!str.isEmpty())\n"
" str.clear();\n"
" f1(str);\n"
" f2(&str);\n"
"}\n")
<< QByteArray("void foo() {\n"
" QString *str = new QString;\n"
" if (!str->isEmpty())\n"
" str->clear();\n"
" f1(*str);\n"
" f2(str);\n"
"}\n");
QTest::newRow("ConvertReferenceToPointer")
<< QByteArray("void foo() {\n"
" QString narf;"
" QString &@str = narf;\n"
" if (!str.isEmpty())\n"
" str.clear();\n"
" f1(str);\n"
" f2(&str);\n"
"}\n")
<< QByteArray("void foo() {\n"
" QString narf;"
" QString *str = &narf;\n"
" if (!str->isEmpty())\n"
" str->clear();\n"
" f1(*str);\n"
" f2(str);\n"
"}\n");
QTest::newRow("ConvertFromPointer_withInitializer")
<< QByteArray("void foo() {\n"
" QString *@str = new QString(QLatin1String(\"schnurz\"));\n"
" if (!str->isEmpty())\n"
" str->clear();\n"
"}\n")
<< QByteArray("void foo() {\n"
" QString str(QLatin1String(\"schnurz\"));\n"
" if (!str.isEmpty())\n"
" str.clear();\n"
"}\n");
QTest::newRow("ConvertFromPointer_withBareInitializer")
<< QByteArray("void foo() {\n"
" QString *@str = new QString;\n"
" if (!str->isEmpty())\n"
" str->clear();\n"
"}\n")
<< QByteArray("void foo() {\n"
" QString str;\n"
" if (!str.isEmpty())\n"
" str.clear();\n"
"}\n");
QTest::newRow("ConvertFromPointer_withEmptyInitializer")
<< QByteArray("void foo() {\n"
" QString *@str = new QString();\n"
" if (!str->isEmpty())\n"
" str->clear();\n"
"}\n")
<< QByteArray("void foo() {\n"
" QString str;\n"
" if (!str.isEmpty())\n"
" str.clear();\n"
"}\n");
QTest::newRow("ConvertFromPointer_structWithPointer")
<< QByteArray("struct Bar{ QString *str; };\n"
"void foo() {\n"
" Bar *@bar = new Bar;\n"
" bar->str = new QString;\n"
" delete bar->str;\n"
" delete bar;\n"
"}\n")
<< QByteArray("struct Bar{ QString *str; };\n"
"void foo() {\n"
" Bar bar;\n"
" bar.str = new QString;\n"
" delete bar.str;\n"
" // delete bar;\n"
"}\n");
QTest::newRow("ConvertToPointer_withInitializer")
<< QByteArray("void foo() {\n"
" QString @str = QLatin1String(\"narf\");\n"
" if (!str.isEmpty())\n"
" str.clear();\n"
"}\n")
<< QByteArray("void foo() {\n"
" QString *str = new QString(QLatin1String(\"narf\"));\n"
" if (!str->isEmpty())\n"
" str->clear();\n"
"}\n");
QTest::newRow("ConvertToPointer_withParenInitializer")
<< QByteArray("void foo() {\n"
" QString @str(QLatin1String(\"narf\"));\n"
" if (!str.isEmpty())\n"
" str.clear();\n"
"}\n")
<< QByteArray("void foo() {\n"
" QString *str = new QString(QLatin1String(\"narf\"));\n"
" if (!str->isEmpty())\n"
" str->clear();\n"
"}\n");
QTest::newRow("ConvertToPointer_noTriggerRValueRefs")
<< QByteArray("void foo(Narf &&@narf) {}\n")
<< QByteArray();
QTest::newRow("ConvertToPointer_noTriggerGlobal")
<< QByteArray("int @global;\n")
<< QByteArray();
QTest::newRow("ConvertToPointer_noTriggerClassMember")
<< QByteArray("struct C { int @member; };\n")
<< QByteArray();
QTest::newRow("ConvertToPointer_noTriggerClassMember2")
<< QByteArray("void f() { struct C { int @member; }; }\n")
<< QByteArray();
QTest::newRow("ConvertToPointer_functionOfFunctionLocalClass")
<< QByteArray("void f() {\n"
" struct C {\n"
" void g() { int @member; }\n"
" };\n"
"}\n")
<< QByteArray("void f() {\n"
" struct C {\n"
" void g() { int *member; }\n"
" };\n"
"}\n");
QTest::newRow("ConvertToPointer_redeclaredVariable_block")
<< QByteArray("void foo() {\n"
" QString @str;\n"
" str.clear();\n"
" {\n"
" QString str;\n"
" str.clear();\n"
" }\n"
" f1(str);\n"
"}\n")
<< QByteArray("void foo() {\n"
" QString *str = new QString;\n"
" str->clear();\n"
" {\n"
" QString str;\n"
" str.clear();\n"
" }\n"
" f1(*str);\n"
"}\n");
QTest::newRow("ConvertAutoFromPointer")
<< QByteArray("void foo() {\n"
" auto @str = new QString(QLatin1String(\"foo\"));\n"
" if (!str->isEmpty())\n"
" str->clear();\n"
" f1(*str);\n"
" f2(str);\n"
"}\n")
<< QByteArray("void foo() {\n"
" auto str = QString(QLatin1String(\"foo\"));\n"
" if (!str.isEmpty())\n"
" str.clear();\n"
" f1(str);\n"
" f2(&str);\n"
"}\n");
QTest::newRow("ConvertAutoFromPointer2")
<< QByteArray("void foo() {\n"
" auto *@str = new QString;\n"
" if (!str->isEmpty())\n"
" str->clear();\n"
" f1(*str);\n"
" f2(str);\n"
"}\n")
<< QByteArray("void foo() {\n"
" auto str = QString();\n"
" if (!str.isEmpty())\n"
" str.clear();\n"
" f1(str);\n"
" f2(&str);\n"
"}\n");
QTest::newRow("ConvertAutoToPointer")
<< QByteArray("void foo() {\n"
" auto @str = QString(QLatin1String(\"foo\"));\n"
" if (!str.isEmpty())\n"
" str.clear();\n"
" f1(str);\n"
" f2(&str);\n"
"}\n")
<< QByteArray("void foo() {\n"
" auto @str = new QString(QLatin1String(\"foo\"));\n"
" if (!str->isEmpty())\n"
" str->clear();\n"
" f1(*str);\n"
" f2(str);\n"
"}\n");
QTest::newRow("ConvertToPointerWithMacro")
<< QByteArray("#define BAR bar\n"
"void func()\n"
"{\n"
" int @foo = 42;\n"
" int bar;\n"
" BAR = foo;\n"
"}\n")
<< QByteArray("#define BAR bar\n"
"void func()\n"
"{\n"
" int *foo = 42;\n"
" int bar;\n"
" BAR = *foo;\n"
"}\n");
QString testObjAndFunc = "struct Object\n"
"{\n"
" Object(%1){}\n"
"};\n"
"void func()\n"
"{\n"
" %2\n"
"}\n";
QTest::newRow("ConvertToStack1_QTCREATORBUG23181")
<< QByteArray(testObjAndFunc.arg("int").arg("Object *@obj = new Object(0);").toUtf8())
<< QByteArray(testObjAndFunc.arg("int").arg("Object obj(0);").toUtf8());
QTest::newRow("ConvertToStack2_QTCREATORBUG23181")
<< QByteArray(testObjAndFunc.arg("int").arg("Object *@obj = new Object{0};").toUtf8())
<< QByteArray(testObjAndFunc.arg("int").arg("Object obj{0};").toUtf8());
QTest::newRow("ConvertToPointer1_QTCREATORBUG23181")
<< QByteArray(testObjAndFunc.arg("").arg("Object @obj;").toUtf8())
<< QByteArray(testObjAndFunc.arg("").arg("Object *obj = new Object;").toUtf8());
QTest::newRow("ConvertToPointer2_QTCREATORBUG23181")
<< QByteArray(testObjAndFunc.arg("").arg("Object @obj();").toUtf8())
<< QByteArray(testObjAndFunc.arg("").arg("Object *obj = new Object();").toUtf8());
QTest::newRow("ConvertToPointer3_QTCREATORBUG23181")
<< QByteArray(testObjAndFunc.arg("").arg("Object @obj{};").toUtf8())
<< QByteArray(testObjAndFunc.arg("").arg("Object *obj = new Object{};").toUtf8());
QTest::newRow("ConvertToPointer4_QTCREATORBUG23181")
<< QByteArray(testObjAndFunc.arg("int").arg("Object @obj(0);").toUtf8())
<< QByteArray(testObjAndFunc.arg("int").arg("Object *obj = new Object(0);").toUtf8());
}
void test()
{
QFETCH(QByteArray, original);
QFETCH(QByteArray, expected);
ConvertFromAndToPointer factory;
QuickFixOperationTest(singleDocument(original, expected), &factory);
}
};
QObject *ConvertFromAndToPointer::createTest() { return new ConvertFromAndToPointerTest; }
#endif // WITH_TESTS
} // namespace
void registerConvertFromAndToPointerQuickfix()
{
CppQuickFixFactory::registerFactory<ConvertFromAndToPointer>();
}
} // namespace CppEditor::Internal
#ifdef WITH_TESTS
#include <convertfromandtopointer.moc>
#endif

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 registerConvertFromAndToPointerQuickfix();
} // namespace CppEditor::Internal

View File

@@ -1039,302 +1039,6 @@ void QuickfixTest::testGeneric_data()
"}" "}"
) << _(); ) << _();
QTest::newRow("ConvertFromPointer")
<< CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
<< _("void foo() {\n"
" QString *@str;\n"
" if (!str->isEmpty())\n"
" str->clear();\n"
" f1(*str);\n"
" f2(str);\n"
"}\n")
<< _("void foo() {\n"
" QString str;\n"
" if (!str.isEmpty())\n"
" str.clear();\n"
" f1(str);\n"
" f2(&str);\n"
"}\n");
QTest::newRow("ConvertToPointer")
<< CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
<< _("void foo() {\n"
" QString @str;\n"
" if (!str.isEmpty())\n"
" str.clear();\n"
" f1(str);\n"
" f2(&str);\n"
"}\n")
<< _("void foo() {\n"
" QString *str = new QString;\n"
" if (!str->isEmpty())\n"
" str->clear();\n"
" f1(*str);\n"
" f2(str);\n"
"}\n");
QTest::newRow("ConvertReferenceToPointer")
<< CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
<< _("void foo() {\n"
" QString narf;"
" QString &@str = narf;\n"
" if (!str.isEmpty())\n"
" str.clear();\n"
" f1(str);\n"
" f2(&str);\n"
"}\n")
<< _("void foo() {\n"
" QString narf;"
" QString *str = &narf;\n"
" if (!str->isEmpty())\n"
" str->clear();\n"
" f1(*str);\n"
" f2(str);\n"
"}\n");
QTest::newRow("ConvertFromPointer_withInitializer")
<< CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
<< _("void foo() {\n"
" QString *@str = new QString(QLatin1String(\"schnurz\"));\n"
" if (!str->isEmpty())\n"
" str->clear();\n"
"}\n")
<< _("void foo() {\n"
" QString str(QLatin1String(\"schnurz\"));\n"
" if (!str.isEmpty())\n"
" str.clear();\n"
"}\n");
QTest::newRow("ConvertFromPointer_withBareInitializer")
<< CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
<< _("void foo() {\n"
" QString *@str = new QString;\n"
" if (!str->isEmpty())\n"
" str->clear();\n"
"}\n")
<< _("void foo() {\n"
" QString str;\n"
" if (!str.isEmpty())\n"
" str.clear();\n"
"}\n");
QTest::newRow("ConvertFromPointer_withEmptyInitializer")
<< CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
<< _("void foo() {\n"
" QString *@str = new QString();\n"
" if (!str->isEmpty())\n"
" str->clear();\n"
"}\n")
<< _("void foo() {\n"
" QString str;\n"
" if (!str.isEmpty())\n"
" str.clear();\n"
"}\n");
QTest::newRow("ConvertFromPointer_structWithPointer")
<< CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
<< _("struct Bar{ QString *str; };\n"
"void foo() {\n"
" Bar *@bar = new Bar;\n"
" bar->str = new QString;\n"
" delete bar->str;\n"
" delete bar;\n"
"}\n")
<< _("struct Bar{ QString *str; };\n"
"void foo() {\n"
" Bar bar;\n"
" bar.str = new QString;\n"
" delete bar.str;\n"
" // delete bar;\n"
"}\n");
QTest::newRow("ConvertToPointer_withInitializer")
<< CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
<< _("void foo() {\n"
" QString @str = QLatin1String(\"narf\");\n"
" if (!str.isEmpty())\n"
" str.clear();\n"
"}\n")
<< _("void foo() {\n"
" QString *str = new QString(QLatin1String(\"narf\"));\n"
" if (!str->isEmpty())\n"
" str->clear();\n"
"}\n");
QTest::newRow("ConvertToPointer_withParenInitializer")
<< CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
<< _("void foo() {\n"
" QString @str(QLatin1String(\"narf\"));\n"
" if (!str.isEmpty())\n"
" str.clear();\n"
"}\n")
<< _("void foo() {\n"
" QString *str = new QString(QLatin1String(\"narf\"));\n"
" if (!str->isEmpty())\n"
" str->clear();\n"
"}\n");
QTest::newRow("ConvertToPointer_noTriggerRValueRefs")
<< CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
<< _("void foo(Narf &&@narf) {}\n")
<< _();
QTest::newRow("ConvertToPointer_noTriggerGlobal")
<< CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
<< _("int @global;\n")
<< _();
QTest::newRow("ConvertToPointer_noTriggerClassMember")
<< CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
<< _("struct C { int @member; };\n")
<< _();
QTest::newRow("ConvertToPointer_noTriggerClassMember2")
<< CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
<< _("void f() { struct C { int @member; }; }\n")
<< _();
QTest::newRow("ConvertToPointer_functionOfFunctionLocalClass")
<< CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
<< _("void f() {\n"
" struct C {\n"
" void g() { int @member; }\n"
" };\n"
"}\n")
<< _("void f() {\n"
" struct C {\n"
" void g() { int *member; }\n"
" };\n"
"}\n");
QTest::newRow("ConvertToPointer_redeclaredVariable_block")
<< CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
<< _("void foo() {\n"
" QString @str;\n"
" str.clear();\n"
" {\n"
" QString str;\n"
" str.clear();\n"
" }\n"
" f1(str);\n"
"}\n")
<< _("void foo() {\n"
" QString *str = new QString;\n"
" str->clear();\n"
" {\n"
" QString str;\n"
" str.clear();\n"
" }\n"
" f1(*str);\n"
"}\n");
QTest::newRow("ConvertAutoFromPointer")
<< CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
<< _("void foo() {\n"
" auto @str = new QString(QLatin1String(\"foo\"));\n"
" if (!str->isEmpty())\n"
" str->clear();\n"
" f1(*str);\n"
" f2(str);\n"
"}\n")
<< _("void foo() {\n"
" auto str = QString(QLatin1String(\"foo\"));\n"
" if (!str.isEmpty())\n"
" str.clear();\n"
" f1(str);\n"
" f2(&str);\n"
"}\n");
QTest::newRow("ConvertAutoFromPointer2")
<< CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
<< _("void foo() {\n"
" auto *@str = new QString;\n"
" if (!str->isEmpty())\n"
" str->clear();\n"
" f1(*str);\n"
" f2(str);\n"
"}\n")
<< _("void foo() {\n"
" auto str = QString();\n"
" if (!str.isEmpty())\n"
" str.clear();\n"
" f1(str);\n"
" f2(&str);\n"
"}\n");
QTest::newRow("ConvertAutoToPointer")
<< CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
<< _("void foo() {\n"
" auto @str = QString(QLatin1String(\"foo\"));\n"
" if (!str.isEmpty())\n"
" str.clear();\n"
" f1(str);\n"
" f2(&str);\n"
"}\n")
<< _("void foo() {\n"
" auto @str = new QString(QLatin1String(\"foo\"));\n"
" if (!str->isEmpty())\n"
" str->clear();\n"
" f1(*str);\n"
" f2(str);\n"
"}\n");
QTest::newRow("ConvertToPointerWithMacro")
<< CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
<< _("#define BAR bar\n"
"void func()\n"
"{\n"
" int @foo = 42;\n"
" int bar;\n"
" BAR = foo;\n"
"}\n")
<< _("#define BAR bar\n"
"void func()\n"
"{\n"
" int *foo = 42;\n"
" int bar;\n"
" BAR = *foo;\n"
"}\n");
QString testObjAndFunc = "struct Object\n"
"{\n"
" Object(%1){}\n"
"};\n"
"void func()\n"
"{\n"
" %2\n"
"}\n";
QTest::newRow("ConvertToStack1_QTCREATORBUG23181")
<< CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
<< _(testObjAndFunc.arg("int").arg("Object *@obj = new Object(0);").toUtf8())
<< _(testObjAndFunc.arg("int").arg("Object obj(0);").toUtf8());
QTest::newRow("ConvertToStack2_QTCREATORBUG23181")
<< CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
<< _(testObjAndFunc.arg("int").arg("Object *@obj = new Object{0};").toUtf8())
<< _(testObjAndFunc.arg("int").arg("Object obj{0};").toUtf8());
QTest::newRow("ConvertToPointer1_QTCREATORBUG23181")
<< CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
<< _(testObjAndFunc.arg("").arg("Object @obj;").toUtf8())
<< _(testObjAndFunc.arg("").arg("Object *obj = new Object;").toUtf8());
QTest::newRow("ConvertToPointer2_QTCREATORBUG23181")
<< CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
<< _(testObjAndFunc.arg("").arg("Object @obj();").toUtf8())
<< _(testObjAndFunc.arg("").arg("Object *obj = new Object();").toUtf8());
QTest::newRow("ConvertToPointer3_QTCREATORBUG23181")
<< CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
<< _(testObjAndFunc.arg("").arg("Object @obj{};").toUtf8())
<< _(testObjAndFunc.arg("").arg("Object *obj = new Object{};").toUtf8());
QTest::newRow("ConvertToPointer4_QTCREATORBUG23181")
<< CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
<< _(testObjAndFunc.arg("int").arg("Object @obj(0);").toUtf8())
<< _(testObjAndFunc.arg("int").arg("Object *obj = new Object(0);").toUtf8());
QTest::newRow("convert to camel case: normal") QTest::newRow("convert to camel case: normal")
<< CppQuickFixFactoryPtr(new ConvertToCamelCase(true)) << CppQuickFixFactoryPtr(new ConvertToCamelCase(true))
<< _("void @lower_case_function();\n") << _("void @lower_case_function();\n")

View File

@@ -14,6 +14,7 @@
#include "../insertionpointlocator.h" #include "../insertionpointlocator.h"
#include "../symbolfinder.h" #include "../symbolfinder.h"
#include "bringidentifierintoscope.h" #include "bringidentifierintoscope.h"
#include "convertfromandtopointer.h"
#include "cppcodegenerationquickfixes.h" #include "cppcodegenerationquickfixes.h"
#include "cppinsertvirtualmethods.h" #include "cppinsertvirtualmethods.h"
#include "cppquickfixassistant.h" #include "cppquickfixassistant.h"
@@ -89,9 +90,7 @@
#include <bitset> #include <bitset>
#include <cctype> #include <cctype>
#include <functional>
#include <limits> #include <limits>
#include <vector>
using namespace CPlusPlus; using namespace CPlusPlus;
using namespace ProjectExplorer; using namespace ProjectExplorer;
@@ -870,368 +869,6 @@ void CompleteSwitchCaseStatement::doMatch(const CppQuickFixInterface &interface,
namespace { namespace {
class ConvertFromAndToPointerOp : public CppQuickFixOperation
{
public:
enum Mode { FromPointer, FromVariable, FromReference };
ConvertFromAndToPointerOp(const CppQuickFixInterface &interface, int priority, Mode mode,
bool isAutoDeclaration,
const SimpleDeclarationAST *simpleDeclaration,
const DeclaratorAST *declaratorAST,
const SimpleNameAST *identifierAST,
Symbol *symbol)
: CppQuickFixOperation(interface, priority)
, m_mode(mode)
, m_isAutoDeclaration(isAutoDeclaration)
, m_simpleDeclaration(simpleDeclaration)
, m_declaratorAST(declaratorAST)
, m_identifierAST(identifierAST)
, m_symbol(symbol)
, m_refactoring(snapshot())
, m_file(m_refactoring.cppFile(filePath()))
, m_document(interface.semanticInfo().doc)
{
setDescription(
mode == FromPointer
? Tr::tr("Convert to Stack Variable")
: Tr::tr("Convert to Pointer"));
}
void perform() override
{
ChangeSet changes;
switch (m_mode) {
case FromPointer:
removePointerOperator(changes);
convertToStackVariable(changes);
break;
case FromReference:
removeReferenceOperator(changes);
Q_FALLTHROUGH();
case FromVariable:
convertToPointer(changes);
break;
}
m_file->setChangeSet(changes);
m_file->apply();
}
private:
void removePointerOperator(ChangeSet &changes) const
{
if (!m_declaratorAST->ptr_operator_list)
return;
PointerAST *ptrAST = m_declaratorAST->ptr_operator_list->value->asPointer();
QTC_ASSERT(ptrAST, return);
const int pos = m_file->startOf(ptrAST->star_token);
changes.remove(pos, pos + 1);
}
void removeReferenceOperator(ChangeSet &changes) const
{
ReferenceAST *refAST = m_declaratorAST->ptr_operator_list->value->asReference();
QTC_ASSERT(refAST, return);
const int pos = m_file->startOf(refAST->reference_token);
changes.remove(pos, pos + 1);
}
void removeNewExpression(ChangeSet &changes, NewExpressionAST *newExprAST) const
{
ExpressionListAST *exprlist = nullptr;
if (newExprAST->new_initializer) {
if (ExpressionListParenAST *ast = newExprAST->new_initializer->asExpressionListParen())
exprlist = ast->expression_list;
else if (BracedInitializerAST *ast = newExprAST->new_initializer->asBracedInitializer())
exprlist = ast->expression_list;
}
if (exprlist) {
// remove 'new' keyword and type before initializer
changes.remove(m_file->startOf(newExprAST->new_token),
m_file->startOf(newExprAST->new_initializer));
changes.remove(m_file->endOf(m_declaratorAST->equal_token - 1),
m_file->startOf(m_declaratorAST->equal_token + 1));
} else {
// remove the whole new expression
changes.remove(m_file->endOf(m_identifierAST->firstToken()),
m_file->startOf(newExprAST->lastToken()));
}
}
void removeNewKeyword(ChangeSet &changes, NewExpressionAST *newExprAST) const
{
// remove 'new' keyword before initializer
changes.remove(m_file->startOf(newExprAST->new_token),
m_file->startOf(newExprAST->new_type_id));
}
void convertToStackVariable(ChangeSet &changes) const
{
// Handle the initializer.
if (m_declaratorAST->initializer) {
if (NewExpressionAST *newExpression = m_declaratorAST->initializer->asNewExpression()) {
if (m_isAutoDeclaration) {
if (!newExpression->new_initializer)
changes.insert(m_file->endOf(newExpression), QStringLiteral("()"));
removeNewKeyword(changes, newExpression);
} else {
removeNewExpression(changes, newExpression);
}
}
}
// Fix all occurrences of the identifier in this function.
ASTPath astPath(m_document);
const QList<SemanticInfo::Use> uses = semanticInfo().localUses.value(m_symbol);
for (const SemanticInfo::Use &use : uses) {
const QList<AST *> path = astPath(use.line, use.column);
AST *idAST = path.last();
bool declarationFound = false;
bool starFound = false;
int ampersandPos = 0;
bool memberAccess = false;
bool deleteCall = false;
for (int i = path.count() - 2; i >= 0; --i) {
if (path.at(i) == m_declaratorAST) {
declarationFound = true;
break;
}
if (MemberAccessAST *memberAccessAST = path.at(i)->asMemberAccess()) {
if (m_file->tokenAt(memberAccessAST->access_token).kind() != T_ARROW)
continue;
int pos = m_file->startOf(memberAccessAST->access_token);
changes.replace(pos, pos + 2, QLatin1String("."));
memberAccess = true;
break;
} else if (DeleteExpressionAST *deleteAST = path.at(i)->asDeleteExpression()) {
const int pos = m_file->startOf(deleteAST->delete_token);
changes.insert(pos, QLatin1String("// "));
deleteCall = true;
break;
} else if (UnaryExpressionAST *unaryExprAST = path.at(i)->asUnaryExpression()) {
const Token tk = m_file->tokenAt(unaryExprAST->unary_op_token);
if (tk.kind() == T_STAR) {
if (!starFound) {
int pos = m_file->startOf(unaryExprAST->unary_op_token);
changes.remove(pos, pos + 1);
}
starFound = true;
} else if (tk.kind() == T_AMPER) {
ampersandPos = m_file->startOf(unaryExprAST->unary_op_token);
}
} else if (PointerAST *ptrAST = path.at(i)->asPointer()) {
if (!starFound) {
const int pos = m_file->startOf(ptrAST->star_token);
changes.remove(pos, pos);
}
starFound = true;
} else if (path.at(i)->asFunctionDefinition()) {
break;
}
}
if (!declarationFound && !starFound && !memberAccess && !deleteCall) {
if (ampersandPos) {
changes.insert(ampersandPos, QLatin1String("&("));
changes.insert(m_file->endOf(idAST->firstToken()), QLatin1String(")"));
} else {
changes.insert(m_file->startOf(idAST), QLatin1String("&"));
}
}
}
}
QString typeNameOfDeclaration() const
{
if (!m_simpleDeclaration
|| !m_simpleDeclaration->decl_specifier_list
|| !m_simpleDeclaration->decl_specifier_list->value) {
return QString();
}
NamedTypeSpecifierAST *namedType
= m_simpleDeclaration->decl_specifier_list->value->asNamedTypeSpecifier();
if (!namedType)
return QString();
Overview overview;
return overview.prettyName(namedType->name->name);
}
void insertNewExpression(ChangeSet &changes, ExpressionAST *ast) const
{
const QString typeName = typeNameOfDeclaration();
if (CallAST *callAST = ast->asCall()) {
if (typeName.isEmpty()) {
changes.insert(m_file->startOf(callAST), QLatin1String("new "));
} else {
changes.insert(m_file->startOf(callAST),
QLatin1String("new ") + typeName + QLatin1Char('('));
changes.insert(m_file->startOf(callAST->lastToken()), QLatin1String(")"));
}
} else {
if (typeName.isEmpty())
return;
changes.insert(m_file->startOf(ast), QLatin1String(" = new ") + typeName);
}
}
void insertNewExpression(ChangeSet &changes) const
{
const QString typeName = typeNameOfDeclaration();
if (typeName.isEmpty())
return;
changes.insert(m_file->endOf(m_identifierAST->firstToken()),
QLatin1String(" = new ") + typeName);
}
void convertToPointer(ChangeSet &changes) const
{
// Handle initializer.
if (m_declaratorAST->initializer) {
if (IdExpressionAST *idExprAST = m_declaratorAST->initializer->asIdExpression()) {
changes.insert(m_file->startOf(idExprAST), QLatin1String("&"));
} else if (CallAST *callAST = m_declaratorAST->initializer->asCall()) {
insertNewExpression(changes, callAST);
} else if (ExpressionListParenAST *exprListAST = m_declaratorAST->initializer
->asExpressionListParen()) {
insertNewExpression(changes, exprListAST);
} else if (BracedInitializerAST *bracedInitializerAST = m_declaratorAST->initializer
->asBracedInitializer()) {
insertNewExpression(changes, bracedInitializerAST);
}
} else {
insertNewExpression(changes);
}
// Fix all occurrences of the identifier in this function.
ASTPath astPath(m_document);
const QList<SemanticInfo::Use> uses = semanticInfo().localUses.value(m_symbol);
for (const SemanticInfo::Use &use : uses) {
const QList<AST *> path = astPath(use.line, use.column);
AST *idAST = path.last();
bool insertStar = true;
for (int i = path.count() - 2; i >= 0; --i) {
if (m_isAutoDeclaration && path.at(i) == m_declaratorAST) {
insertStar = false;
break;
}
if (MemberAccessAST *memberAccessAST = path.at(i)->asMemberAccess()) {
const int pos = m_file->startOf(memberAccessAST->access_token);
changes.replace(pos, pos + 1, QLatin1String("->"));
insertStar = false;
break;
} else if (UnaryExpressionAST *unaryExprAST = path.at(i)->asUnaryExpression()) {
if (m_file->tokenAt(unaryExprAST->unary_op_token).kind() == T_AMPER) {
const int pos = m_file->startOf(unaryExprAST->unary_op_token);
changes.remove(pos, pos + 1);
insertStar = false;
break;
}
} else if (path.at(i)->asFunctionDefinition()) {
break;
}
}
if (insertStar)
changes.insert(m_file->startOf(idAST), QLatin1String("*"));
}
}
const Mode m_mode;
const bool m_isAutoDeclaration;
const SimpleDeclarationAST * const m_simpleDeclaration;
const DeclaratorAST * const m_declaratorAST;
const SimpleNameAST * const m_identifierAST;
Symbol * const m_symbol;
const CppRefactoringChanges m_refactoring;
const CppRefactoringFilePtr m_file;
const Document::Ptr m_document;
};
} // anonymous namespace
void ConvertFromAndToPointer::doMatch(const CppQuickFixInterface &interface,
QuickFixOperations &result)
{
const QList<AST *> &path = interface.path();
if (path.count() < 2)
return;
SimpleNameAST *identifier = path.last()->asSimpleName();
if (!identifier)
return;
SimpleDeclarationAST *simpleDeclaration = nullptr;
DeclaratorAST *declarator = nullptr;
bool isFunctionLocal = false;
bool isClassLocal = false;
ConvertFromAndToPointerOp::Mode mode = ConvertFromAndToPointerOp::FromVariable;
for (int i = path.count() - 2; i >= 0; --i) {
AST *ast = path.at(i);
if (!declarator && (declarator = ast->asDeclarator()))
continue;
if (!simpleDeclaration && (simpleDeclaration = ast->asSimpleDeclaration()))
continue;
if (declarator && simpleDeclaration) {
if (ast->asClassSpecifier()) {
isClassLocal = true;
} else if (ast->asFunctionDefinition() && !isClassLocal) {
isFunctionLocal = true;
break;
}
}
}
if (!isFunctionLocal || !simpleDeclaration || !declarator)
return;
Symbol *symbol = nullptr;
for (List<Symbol *> *lst = simpleDeclaration->symbols; lst; lst = lst->next) {
if (lst->value->name() == identifier->name) {
symbol = lst->value;
break;
}
}
if (!symbol)
return;
bool isAutoDeclaration = false;
if (symbol->storage() == Symbol::Auto) {
// For auto variables we must deduce the type from the initializer.
if (!declarator->initializer)
return;
isAutoDeclaration = true;
TypeOfExpression typeOfExpression;
typeOfExpression.init(interface.semanticInfo().doc, interface.snapshot());
typeOfExpression.setExpandTemplates(true);
CppRefactoringFilePtr file = interface.currentFile();
Scope *scope = file->scopeAt(declarator->firstToken());
QList<LookupItem> result = typeOfExpression(file->textOf(declarator->initializer).toUtf8(),
scope, TypeOfExpression::Preprocess);
if (!result.isEmpty() && result.first().type()->asPointerType())
mode = ConvertFromAndToPointerOp::FromPointer;
} else if (declarator->ptr_operator_list) {
for (PtrOperatorListAST *ops = declarator->ptr_operator_list; ops; ops = ops->next) {
if (ops != declarator->ptr_operator_list) {
// Bail out on more complex pointer types (e.g. pointer of pointer,
// or reference of pointer).
return;
}
if (ops->value->asPointer())
mode = ConvertFromAndToPointerOp::FromPointer;
else if (ops->value->asReference())
mode = ConvertFromAndToPointerOp::FromReference;
}
}
const int priority = path.size() - 1;
result << new ConvertFromAndToPointerOp(interface, priority, mode, isAutoDeclaration,
simpleDeclaration, declarator, identifier, symbol);
}
namespace {
class ApplyDeclDefLinkOperation : public CppQuickFixOperation class ApplyDeclDefLinkOperation : public CppQuickFixOperation
{ {
public: public:
@@ -1645,7 +1282,6 @@ void createCppQuickFixes()
new CompleteSwitchCaseStatement; new CompleteSwitchCaseStatement;
new ApplyDeclDefLinkChanges; new ApplyDeclDefLinkChanges;
new ConvertFromAndToPointer;
new AssignToLocalVariable; new AssignToLocalVariable;
@@ -1664,6 +1300,7 @@ void createCppQuickFixes()
registerRewriteCommentQuickfixes(); registerRewriteCommentQuickfixes();
registerExtractFunctionQuickfix(); registerExtractFunctionQuickfix();
registerExtractLiteralAsParameterQuickfix(); registerExtractLiteralAsParameterQuickfix();
registerConvertFromAndToPointerQuickfix();
new ExtraRefactoringOperations; new ExtraRefactoringOperations;

View File

@@ -125,17 +125,6 @@ private:
void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override; void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override;
}; };
/*!
Converts the selected variable to a pointer if it is a stack variable or reference, or vice versa.
Activates on variable declarations.
*/
class ConvertFromAndToPointer : public CppQuickFixFactory
{
public:
void doMatch(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override;
};
/*! /*!
Applies function signature changes Applies function signature changes
*/ */