CppEditor: make convert from/to pointer aware of auto

Change-Id: Ic378ea1f7fc58fdb9351c9eaa9e9602e568d5ac9
Task-number: QTCREATORBUG-13605
Reviewed-by: Robert Loehning <robert.loehning@theqtcompany.com>
Reviewed-by: Nikolai Kosjar <nikolai.kosjar@theqtcompany.com>
This commit is contained in:
Joerg Bornemann
2014-12-12 12:13:56 +01:00
parent 7386ac3509
commit c64a3891a2
2 changed files with 108 additions and 11 deletions

View File

@@ -1497,6 +1497,57 @@ void CppEditorPlugin::test_quickfix_data()
" f1(*str);\n" " f1(*str);\n"
"}\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("InsertQtPropertyMembers_noTriggerInvalidCode") QTest::newRow("InsertQtPropertyMembers_noTriggerInvalidCode")
<< CppQuickFixFactoryPtr(new InsertQtPropertyMembers) << CppQuickFixFactoryPtr(new InsertQtPropertyMembers)
<< _("class C { @Q_PROPERTY(typeid foo READ foo) };\n") << _("class C { @Q_PROPERTY(typeid foo READ foo) };\n")

View File

@@ -3862,12 +3862,14 @@ public:
enum Mode { FromPointer, FromVariable, FromReference }; enum Mode { FromPointer, FromVariable, FromReference };
ConvertFromAndToPointerOp(const CppQuickFixInterface &interface, int priority, Mode mode, ConvertFromAndToPointerOp(const CppQuickFixInterface &interface, int priority, Mode mode,
bool isAutoDeclaration,
const SimpleDeclarationAST *simpleDeclaration, const SimpleDeclarationAST *simpleDeclaration,
const DeclaratorAST *declaratorAST, const DeclaratorAST *declaratorAST,
const SimpleNameAST *identifierAST, const SimpleNameAST *identifierAST,
Symbol *symbol) Symbol *symbol)
: CppQuickFixOperation(interface, priority) : CppQuickFixOperation(interface, priority)
, m_mode(mode) , m_mode(mode)
, m_isAutoDeclaration(isAutoDeclaration)
, m_simpleDeclaration(simpleDeclaration) , m_simpleDeclaration(simpleDeclaration)
, m_declaratorAST(declaratorAST) , m_declaratorAST(declaratorAST)
, m_identifierAST(identifierAST) , m_identifierAST(identifierAST)
@@ -3906,6 +3908,8 @@ public:
private: private:
void removePointerOperator(ChangeSet &changes) const void removePointerOperator(ChangeSet &changes) const
{ {
if (!m_declaratorAST->ptr_operator_list)
return;
PointerAST *ptrAST = m_declaratorAST->ptr_operator_list->value->asPointer(); PointerAST *ptrAST = m_declaratorAST->ptr_operator_list->value->asPointer();
QTC_ASSERT(ptrAST, return); QTC_ASSERT(ptrAST, return);
const int pos = m_file->startOf(ptrAST->star_token); const int pos = m_file->startOf(ptrAST->star_token);
@@ -3942,12 +3946,26 @@ private:
} }
} }
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 void convertToStackVariable(ChangeSet &changes) const
{ {
// Handle the initializer. // Handle the initializer.
if (m_declaratorAST->initializer) { if (m_declaratorAST->initializer) {
if (NewExpressionAST *newExpression = m_declaratorAST->initializer->asNewExpression()) if (NewExpressionAST *newExpression = m_declaratorAST->initializer->asNewExpression()) {
removeNewExpression(changes, newExpression); 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. // Fix all occurrences of the identifier in this function.
@@ -3955,10 +3973,15 @@ private:
foreach (const SemanticInfo::Use &use, semanticInfo().localUses.value(m_symbol)) { foreach (const SemanticInfo::Use &use, semanticInfo().localUses.value(m_symbol)) {
const QList<AST *> path = astPath(use.line, use.column); const QList<AST *> path = astPath(use.line, use.column);
AST *idAST = path.last(); AST *idAST = path.last();
bool declarationFound = false;
bool starFound = false; bool starFound = false;
int ampersandPos = 0; int ampersandPos = 0;
bool memberAccess = false; bool memberAccess = false;
for (int i = path.count() - 2; i >= 0; --i) { 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 (MemberAccessAST *memberAccessAST = path.at(i)->asMemberAccess()) {
if (m_file->tokenAt(memberAccessAST->access_token).kind() != T_ARROW) if (m_file->tokenAt(memberAccessAST->access_token).kind() != T_ARROW)
continue; continue;
@@ -3987,7 +4010,7 @@ private:
break; break;
} }
} }
if (!starFound && !memberAccess) { if (!declarationFound && !starFound && !memberAccess) {
if (ampersandPos) { if (ampersandPos) {
changes.insert(ampersandPos, QLatin1String("&(")); changes.insert(ampersandPos, QLatin1String("&("));
changes.insert(m_file->endOf(idAST->firstToken()), QLatin1String(")")); changes.insert(m_file->endOf(idAST->firstToken()), QLatin1String(")"));
@@ -4017,11 +4040,13 @@ private:
void insertNewExpression(ChangeSet &changes, CallAST *callAST) const void insertNewExpression(ChangeSet &changes, CallAST *callAST) const
{ {
const QString typeName = typeNameOfDeclaration(); const QString typeName = typeNameOfDeclaration();
if (typeName.isEmpty()) if (typeName.isEmpty()) {
return; changes.insert(m_file->startOf(callAST), QLatin1String("new "));
changes.insert(m_file->startOf(callAST), } else {
QLatin1String("new ") + typeName + QLatin1Char('(')); changes.insert(m_file->startOf(callAST),
changes.insert(m_file->startOf(callAST->lastToken()), QLatin1String(")")); QLatin1String("new ") + typeName + QLatin1Char('('));
changes.insert(m_file->startOf(callAST->lastToken()), QLatin1String(")"));
}
} }
void insertNewExpression(ChangeSet &changes, ExpressionListParenAST *exprListAST) const void insertNewExpression(ChangeSet &changes, ExpressionListParenAST *exprListAST) const
@@ -4054,6 +4079,10 @@ private:
AST *idAST = path.last(); AST *idAST = path.last();
bool insertStar = true; bool insertStar = true;
for (int i = path.count() - 2; i >= 0; --i) { 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()) { if (MemberAccessAST *memberAccessAST = path.at(i)->asMemberAccess()) {
const int pos = m_file->startOf(memberAccessAST->access_token); const int pos = m_file->startOf(memberAccessAST->access_token);
changes.replace(pos, pos + 1, QLatin1String("->")); changes.replace(pos, pos + 1, QLatin1String("->"));
@@ -4076,6 +4105,7 @@ private:
} }
const Mode m_mode; const Mode m_mode;
const bool m_isAutoDeclaration;
const SimpleDeclarationAST * const m_simpleDeclaration; const SimpleDeclarationAST * const m_simpleDeclaration;
const DeclaratorAST * const m_declaratorAST; const DeclaratorAST * const m_declaratorAST;
const SimpleNameAST * const m_identifierAST; const SimpleNameAST * const m_identifierAST;
@@ -4129,7 +4159,23 @@ void ConvertFromAndToPointer::match(const CppQuickFixInterface &interface,
if (!symbol) if (!symbol)
return; return;
if (declarator->ptr_operator_list) { 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()->isPointerType())
mode = ConvertFromAndToPointerOp::FromPointer;
} else if (declarator->ptr_operator_list) {
for (PtrOperatorListAST *ops = declarator->ptr_operator_list; ops; ops = ops->next) { for (PtrOperatorListAST *ops = declarator->ptr_operator_list; ops; ops = ops->next) {
if (ops != declarator->ptr_operator_list) { if (ops != declarator->ptr_operator_list) {
// Bail out on more complex pointer types (e.g. pointer of pointer, // Bail out on more complex pointer types (e.g. pointer of pointer,
@@ -4144,8 +4190,8 @@ void ConvertFromAndToPointer::match(const CppQuickFixInterface &interface,
} }
const int priority = path.size() - 1; const int priority = path.size() - 1;
result.append(new ConvertFromAndToPointerOp(interface, priority, mode, simpleDeclaration, result.append(new ConvertFromAndToPointerOp(interface, priority, mode, isAutoDeclaration,
declarator, identifier, symbol)); simpleDeclaration, declarator, identifier, symbol));
} }
namespace { namespace {