forked from qt-creator/qt-creator
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:
@@ -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
|
||||||
|
@@ -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",
|
||||||
|
705
src/plugins/cppeditor/quickfixes/convertfromandtopointer.cpp
Normal file
705
src/plugins/cppeditor/quickfixes/convertfromandtopointer.cpp
Normal 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
|
@@ -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
|
@@ -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")
|
||||||
|
@@ -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;
|
||||||
|
|
||||||
|
@@ -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
|
||||||
*/
|
*/
|
||||||
|
Reference in New Issue
Block a user