summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikolai Kosjar <nikolai.kosjar@digia.com>2013-01-14 14:45:36 +0100
committerNikolai Kosjar <nikolai.kosjar@digia.com>2013-01-24 11:59:14 +0100
commit138066792e61925b827d9d722669b8d96bc0a87b (patch)
treec05ebd5e6aab68ed48b8dd95ac8b05c12ee1327c
parentfa7ab13f300b49122f3f951c8995ec88041f1ddb (diff)
downloadqt-creator-138066792e61925b827d9d722669b8d96bc0a87b.tar.gz
C++: Introduce PointerDeclarationFormatter
For a given AST, CppRefactoringFile and Overview this will create a ChangeSet for rewriting the pointer or reference declaration according to the Overview. Task-number: QTCREATORBUG-6169 Change-Id: If6f824c1ea5e9f53a11a58ec8b6d696d01f0723e Reviewed-by: Erik Verbruggen <erik.verbruggen@digia.com>
-rw-r--r--src/libs/3rdparty/cplusplus/Bind.cpp7
-rw-r--r--src/libs/3rdparty/cplusplus/Bind.h1
-rw-r--r--src/libs/cplusplus/CppDocument.cpp2
-rw-r--r--src/libs/cplusplus/Overview.h3
-rw-r--r--src/plugins/cpptools/cpppointerdeclarationformatter.cpp417
-rw-r--r--src/plugins/cpptools/cpppointerdeclarationformatter.h122
-rw-r--r--src/plugins/cpptools/cpppointerdeclarationformatter_test.cpp577
-rw-r--r--src/plugins/cpptools/cpptools.pro9
-rw-r--r--src/plugins/cpptools/cpptools.qbs5
-rw-r--r--src/plugins/cpptools/cpptoolsplugin.h12
10 files changed, 1146 insertions, 9 deletions
diff --git a/src/libs/3rdparty/cplusplus/Bind.cpp b/src/libs/3rdparty/cplusplus/Bind.cpp
index 909ef24e78..98310f9ee8 100644
--- a/src/libs/3rdparty/cplusplus/Bind.cpp
+++ b/src/libs/3rdparty/cplusplus/Bind.cpp
@@ -181,6 +181,13 @@ void Bind::operator()(DeclarationAST *ast, Scope *scope)
(void) switchScope(previousScope);
}
+void Bind::operator()(StatementAST *ast, Scope *scope)
+{
+ Scope *previousScope = switchScope(scope);
+ statement(ast);
+ (void) switchScope(previousScope);
+}
+
FullySpecifiedType Bind::operator()(ExpressionAST *ast, Scope *scope)
{
Scope *previousScope = switchScope(scope);
diff --git a/src/libs/3rdparty/cplusplus/Bind.h b/src/libs/3rdparty/cplusplus/Bind.h
index 93022b9af7..ca2c6084ba 100644
--- a/src/libs/3rdparty/cplusplus/Bind.h
+++ b/src/libs/3rdparty/cplusplus/Bind.h
@@ -34,6 +34,7 @@ public:
void operator()(TranslationUnitAST *ast, Namespace *globalNamespace);
void operator()(DeclarationAST *ast, Scope *scope);
+ void operator()(StatementAST *ast, Scope *scope);
FullySpecifiedType operator()(ExpressionAST *ast, Scope *scope);
FullySpecifiedType operator()(NewTypeIdAST *ast, Scope *scope);
diff --git a/src/libs/cplusplus/CppDocument.cpp b/src/libs/cplusplus/CppDocument.cpp
index 2069dd9dc3..ed8b0a6699 100644
--- a/src/libs/cplusplus/CppDocument.cpp
+++ b/src/libs/cplusplus/CppDocument.cpp
@@ -595,6 +595,8 @@ void Document::check(CheckMode mode)
if (TranslationUnitAST *ast = _translationUnit->ast()->asTranslationUnit())
semantic(ast, _globalNamespace);
+ else if (StatementAST *ast = _translationUnit->ast()->asStatement())
+ semantic(ast, _globalNamespace);
else if (ExpressionAST *ast = _translationUnit->ast()->asExpression())
semantic(ast, _globalNamespace);
else if (DeclarationAST *ast = translationUnit()->ast()->asDeclaration())
diff --git a/src/libs/cplusplus/Overview.h b/src/libs/cplusplus/Overview.h
index 339a6d8fc7..405bbb2b46 100644
--- a/src/libs/cplusplus/Overview.h
+++ b/src/libs/cplusplus/Overview.h
@@ -49,9 +49,6 @@ namespace CPlusPlus {
class CPLUSPLUS_EXPORT Overview
{
- Overview(const Overview &other);
- void operator =(const Overview &other);
-
public:
Overview();
diff --git a/src/plugins/cpptools/cpppointerdeclarationformatter.cpp b/src/plugins/cpptools/cpppointerdeclarationformatter.cpp
new file mode 100644
index 0000000000..ba0fa4c60f
--- /dev/null
+++ b/src/plugins/cpptools/cpppointerdeclarationformatter.cpp
@@ -0,0 +1,417 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+
+#include "cpppointerdeclarationformatter.h"
+
+#include <AST.h>
+
+#include <QTextCursor>
+
+#define DEBUG_OUTPUT 0
+#define CHECK_RV(cond, err, r) if (!(cond)) { if (DEBUG_OUTPUT) qDebug() << (err); return r; }
+#define CHECK_R(cond, err) if (!(cond)) { if (DEBUG_OUTPUT) qDebug() << (err); return; }
+#define CHECK_C(cond, err) if (!(cond)) { if (DEBUG_OUTPUT) qDebug() << (err); continue; }
+
+using namespace CppTools;
+
+/*!
+ \brief Skip not type relevant specifiers and return the index of the
+ first specifier token which is not followed by __attribute__
+ ((T___ATTRIBUTE__)).
+
+ This is used to get 'correct' start of the activation range in
+ simple declarations.
+
+ Consider these cases:
+
+ static char *s = 0;
+ typedef char *s cp;
+ __attribute__((visibility("default"))) char *f();
+
+ For all cases we want to skip all the not type relevant specifer
+ (since these are not part of the type and thus are not rewritten).
+
+ \param list The specifier list to iterate
+ \param translationUnit The TranslationUnit
+ \param endToken Do not check further than this token
+ \param found Output parameter, must not be 0.
+ */
+static unsigned firstTypeSpecifierWithoutFollowingAttribute(
+ SpecifierListAST *list, TranslationUnit *translationUnit, unsigned endToken, bool *found)
+{
+ *found = false;
+ if (! list || ! translationUnit || ! endToken)
+ return 0;
+
+ for (SpecifierListAST *it = list; it; it = it->next) {
+ SpecifierAST *specifier = it->value;
+ CHECK_RV(specifier, "No specifier", 0);
+ const unsigned index = specifier->firstToken();
+ CHECK_RV(index < endToken, "EndToken reached", 0);
+
+ const int tokenKind = translationUnit->tokenKind(index);
+ switch (tokenKind) {
+ case T_VIRTUAL:
+ case T_INLINE:
+ case T_FRIEND:
+ case T_REGISTER:
+ case T_STATIC:
+ case T_EXTERN:
+ case T_MUTABLE:
+ case T_TYPEDEF:
+ case T_CONSTEXPR:
+ case T___ATTRIBUTE__:
+ continue;
+ default:
+ // Check if attributes follow
+ for (unsigned i = index; i <= endToken; ++i) {
+ const int tokenKind = translationUnit->tokenKind(i);
+ if (tokenKind == T___ATTRIBUTE__)
+ return 0;
+ }
+ *found = true;
+ return index;
+ }
+ }
+
+ return 0;
+}
+
+PointerDeclarationFormatter::PointerDeclarationFormatter(
+ const CppRefactoringFilePtr refactoringFile,
+ const Overview &overview,
+ CursorHandling cursorHandling)
+ : ASTVisitor(refactoringFile->cppDocument()->translationUnit())
+ , m_cppRefactoringFile(refactoringFile)
+ , m_overview(overview)
+ , m_cursorHandling(cursorHandling)
+{}
+
+/*!
+ Handle
+ (1) Simple declarations like in "char *s, *t, *int foo();"
+ (2) Return types of function declarations.
+ */
+bool PointerDeclarationFormatter::visit(SimpleDeclarationAST *ast)
+{
+ CHECK_RV(ast, "Invalid AST", true);
+
+ DeclaratorListAST *declaratorList = ast->declarator_list;
+ CHECK_RV(declaratorList, "No declarator list", true);
+ DeclaratorAST *firstDeclarator = declaratorList->value;
+ CHECK_RV(firstDeclarator, "No declarator", true);
+ CHECK_RV(ast->symbols, "No Symbols", true);
+ CHECK_RV(ast->symbols->value, "No Symbol", true);
+
+ List<Symbol *> *sit = ast->symbols;
+ DeclaratorListAST *dit = declaratorList;
+ for (; sit && dit; sit = sit->next, dit = dit->next) {
+ DeclaratorAST *declarator = dit->value;
+ Symbol *symbol = sit->value;
+
+ const bool isFirstDeclarator = declarator == firstDeclarator;
+
+ // If were not handling the first declarator, we need to remove
+ // characters from the beginning since our rewritten declaration
+ // will contain all type specifiers.
+ int charactersToRemove = 0;
+ if (! isFirstDeclarator) {
+ const int startAST = m_cppRefactoringFile->startOf(ast);
+ const int startFirstDeclarator = m_cppRefactoringFile->startOf(firstDeclarator);
+ CHECK_RV(startAST < startFirstDeclarator, "No specifier", true);
+ charactersToRemove = startFirstDeclarator - startAST;
+ }
+
+ // Specify activation range
+ int lastActivationToken = 0;
+ Range range;
+ // (2) Handle function declaration's return type
+ if (symbol->type()->asFunctionType()) {
+ PostfixDeclaratorListAST *pfDeclaratorList = declarator->postfix_declarator_list;
+ CHECK_RV(pfDeclaratorList, "No postfix declarator list", true);
+ PostfixDeclaratorAST *pfDeclarator = pfDeclaratorList->value;
+ CHECK_RV(pfDeclarator, "No postfix declarator", true);
+ FunctionDeclaratorAST *functionDeclarator = pfDeclarator->asFunctionDeclarator();
+ CHECK_RV(functionDeclarator, "No function declarator", true);
+ // End the activation range before the '(' token.
+ lastActivationToken = functionDeclarator->lparen_token - 1;
+
+ SpecifierListAST *specifierList = isFirstDeclarator
+ ? ast->decl_specifier_list
+ : declarator->attribute_list;
+
+ unsigned firstActivationToken = 0;
+ bool foundBegin = false;
+ firstActivationToken = firstTypeSpecifierWithoutFollowingAttribute(
+ specifierList,
+ m_cppRefactoringFile->cppDocument()->translationUnit(),
+ lastActivationToken,
+ &foundBegin);
+ if (! foundBegin) {
+ CHECK_RV(! isFirstDeclarator, "Declaration without attributes not supported", true);
+ firstActivationToken = declarator->firstToken();
+ }
+
+ range.start = m_cppRefactoringFile->startOf(firstActivationToken);
+
+ // (1) Handle 'normal' declarations.
+ } else {
+ if (isFirstDeclarator) {
+ bool foundBegin = false;
+ unsigned firstActivationToken = firstTypeSpecifierWithoutFollowingAttribute(
+ ast->decl_specifier_list,
+ m_cppRefactoringFile->cppDocument()->translationUnit(),
+ declarator->firstToken(),
+ &foundBegin);
+ CHECK_RV(foundBegin, "Declaration without attributes not supported", true);
+ range.start = m_cppRefactoringFile->startOf(firstActivationToken);
+ } else {
+ range.start = m_cppRefactoringFile->startOf(declarator);
+ }
+ lastActivationToken = declarator->equal_token
+ ? declarator->equal_token - 1
+ : declarator->lastToken() - 1;
+ }
+ range.end = m_cppRefactoringFile->endOf(lastActivationToken);
+
+ checkAndRewrite(symbol, range, charactersToRemove);
+ }
+ return true;
+}
+
+/*! Handle return types of function definitions */
+bool PointerDeclarationFormatter::visit(FunctionDefinitionAST *ast)
+{
+ DeclaratorAST *declarator = ast->declarator;
+ CHECK_RV(declarator, "No declarator", true);
+ CHECK_RV(declarator->ptr_operator_list, "No Pointer or references", true);
+ Symbol *symbol = ast->symbol;
+
+ PostfixDeclaratorListAST *pfDeclaratorList = declarator->postfix_declarator_list;
+ CHECK_RV(pfDeclaratorList, "No postfix declarator list", true);
+ PostfixDeclaratorAST *pfDeclarator = pfDeclaratorList->value;
+ CHECK_RV(pfDeclarator, "No postfix declarator", true);
+ FunctionDeclaratorAST *functionDeclarator = pfDeclarator->asFunctionDeclarator();
+ CHECK_RV(functionDeclarator, "No function declarator", true);
+
+ // Specify activation range
+ bool foundBegin = false;
+ const unsigned lastActivationToken = functionDeclarator->lparen_token - 1;
+ const unsigned firstActivationToken = firstTypeSpecifierWithoutFollowingAttribute(
+ ast->decl_specifier_list,
+ m_cppRefactoringFile->cppDocument()->translationUnit(),
+ lastActivationToken,
+ &foundBegin);
+ CHECK_RV(foundBegin, "Declaration without attributes not supported", true);
+ Range range(m_cppRefactoringFile->startOf(firstActivationToken),
+ m_cppRefactoringFile->endOf(lastActivationToken));
+
+ checkAndRewrite(symbol, range);
+ return true;
+}
+
+/*! Handle parameters in function declarations and definitions */
+bool PointerDeclarationFormatter::visit(ParameterDeclarationAST *ast)
+{
+ DeclaratorAST *declarator = ast->declarator;
+ CHECK_RV(declarator, "No declarator", true);
+ CHECK_RV(declarator->ptr_operator_list, "No Pointer or references", true);
+ Symbol *symbol = ast->symbol;
+
+ // Specify activation range
+ const int lastActivationToken = ast->equal_token
+ ? ast->equal_token - 1
+ : ast->lastToken() - 1;
+ Range range(m_cppRefactoringFile->startOf(ast),
+ m_cppRefactoringFile->endOf(lastActivationToken));
+
+ checkAndRewrite(symbol, range);
+ return true;
+}
+
+/*! Handle declaration in foreach statement */
+bool PointerDeclarationFormatter::visit(ForeachStatementAST *ast)
+{
+ DeclaratorAST *declarator = ast->declarator;
+ CHECK_RV(declarator, "No declarator", true);
+ CHECK_RV(declarator->ptr_operator_list, "No Pointer or references", true);
+ CHECK_RV(ast->type_specifier_list, "No type specifier", true);
+ SpecifierAST *firstSpecifier = ast->type_specifier_list->value;
+ CHECK_RV(firstSpecifier, "No first type specifier", true);
+ CHECK_RV(ast->symbol, "No symbol", true);
+ Symbol *symbol = ast->symbol->memberAt(0);
+
+ // Specify activation range
+ const int lastActivationToken = declarator->equal_token
+ ? declarator->equal_token - 1
+ : declarator->lastToken() - 1;
+ Range range(m_cppRefactoringFile->startOf(firstSpecifier),
+ m_cppRefactoringFile->endOf(lastActivationToken));
+
+ checkAndRewrite(symbol, range);
+ return true;
+}
+
+bool PointerDeclarationFormatter::visit(IfStatementAST *ast)
+{
+ processIfWhileForStatement(ast->condition, ast->symbol);
+ return true;
+}
+
+bool PointerDeclarationFormatter::visit(WhileStatementAST *ast)
+{
+ processIfWhileForStatement(ast->condition, ast->symbol);
+ return true;
+}
+
+bool PointerDeclarationFormatter::visit(ForStatementAST *ast)
+{
+ processIfWhileForStatement(ast->condition, ast->symbol);
+ return true;
+}
+
+/*! Handle declaration in if, while and for statements */
+void PointerDeclarationFormatter::processIfWhileForStatement(ExpressionAST *expression,
+ Symbol *statementSymbol)
+{
+ CHECK_R(expression, "No expression");
+ CHECK_R(statementSymbol, "No symbol");
+
+ ConditionAST *condition = expression->asCondition();
+ CHECK_R(condition, "No condition");
+ DeclaratorAST *declarator = condition->declarator;
+ CHECK_R(declarator, "No declarator");
+ CHECK_R(declarator->ptr_operator_list, "No Pointer or references");
+ CHECK_R(declarator->equal_token, "No equal token");
+ Block *block = statementSymbol->asBlock();
+ CHECK_R(block, "No block");
+ CHECK_R(block->memberCount() > 0, "No block members");
+
+ // Get the right symbol
+ //
+ // This is especially important for e.g.
+ //
+ // for (char *s = 0; char *t = 0;) {}
+ //
+ // The declaration for 's' will be handled in visit(SimpleDeclarationAST *ast),
+ // so handle declaration for 't' here.
+ Scope::iterator it = block->lastMember() - 1;
+ Symbol *symbol = *it;
+ if (symbol && symbol->asScope()) { // True if there is a "{ ... }" following.
+ --it;
+ symbol = *it;
+ }
+
+ // Specify activation range
+ Range range(m_cppRefactoringFile->startOf(condition),
+ m_cppRefactoringFile->endOf(declarator->equal_token - 1));
+
+ checkAndRewrite(symbol, range);
+}
+
+/*!
+ \brief Do some further checks and rewrite the symbol's type and
+ name into the given range
+
+ \param symbol the symbol to be rewritten
+ \param range the substitution range in the file
+ */
+void PointerDeclarationFormatter::checkAndRewrite(Symbol *symbol, Range range,
+ unsigned charactersToRemove)
+{
+ CHECK_R(range.start >= 0 && range.end > 0, "Range invalid");
+ CHECK_R(range.start < range.end, "Range invalid");
+ CHECK_R(symbol, "No symbol");
+
+ // Check range with respect to cursor position / selection
+ if (m_cursorHandling == RespectCursor) {
+ const QTextCursor cursor = m_cppRefactoringFile->cursor();
+ if (cursor.hasSelection()) {
+ CHECK_R(cursor.selectionStart() <= range.start, "Change not in selection range");
+ CHECK_R(range.end <= cursor.selectionEnd(), "Change not in selection range");
+ } else {
+ CHECK_R(range.start <= cursor.selectionStart(), "Cursor before activation range");
+ CHECK_R(cursor.selectionEnd() <= range.end, "Cursor after activation range");
+ }
+ }
+
+ FullySpecifiedType type = symbol->type();
+ if (Function *function = type->asFunctionType())
+ type = function->returnType();
+
+ // Check if pointers or references are involved
+ const QString originalDeclaration = m_cppRefactoringFile->textOf(range);
+ CHECK_R(originalDeclaration.contains(QLatin1Char('&'))
+ || originalDeclaration.contains(QLatin1Char('*')), "No pointer or references");
+
+ // Does the rewritten declaration (part) differs from the original source (part)?
+ QString rewrittenDeclaration = rewriteDeclaration(type, symbol->name());
+ rewrittenDeclaration.remove(0, charactersToRemove);
+ CHECK_R(originalDeclaration != rewrittenDeclaration, "Rewritten is same as original");
+
+ if (DEBUG_OUTPUT) {
+ qDebug("Rewritten: \"%s\" --> \"%s\"", originalDeclaration.toLatin1().constData(),
+ rewrittenDeclaration.toLatin1().constData());
+ }
+
+ // Creating the replacement in the changeset may fail due to operations
+ // in the changeset that overlap with the current range.
+ //
+ // Consider this case:
+ //
+ // void (*foo)(char * s) = 0;
+ //
+ // First visit(SimpleDeclarationAST *ast) will be called. It creates a
+ // replacement that also includes the parameter.
+ // Next visit(ParameterDeclarationAST *ast) is called with the
+ // original source. It tries to create an replacement operation
+ // at this position and fails due to overlapping ranges (the
+ // simple declaration range includes parameter declaration range).
+ ChangeSet change(m_changeSet);
+ if (change.replace(range, rewrittenDeclaration))
+ m_changeSet = change;
+ else if (DEBUG_OUTPUT)
+ qDebug() << "Replacement operation failed";
+}
+
+/*! Rewrite/format the given type and name. */
+QString PointerDeclarationFormatter::rewriteDeclaration(FullySpecifiedType type, const Name *name)
+ const
+{
+ CHECK_RV(type.isValid(), "Invalid type", QString());
+
+ const char *identifier = 0;
+ if (const Name *declarationName = name) {
+ if (const Identifier *id = declarationName->identifier())
+ identifier = id->chars();
+ }
+
+ return m_overview.prettyType(type, QLatin1String(identifier));
+}
diff --git a/src/plugins/cpptools/cpppointerdeclarationformatter.h b/src/plugins/cpptools/cpppointerdeclarationformatter.h
new file mode 100644
index 0000000000..214e72396c
--- /dev/null
+++ b/src/plugins/cpptools/cpppointerdeclarationformatter.h
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef CPPPOINTERFORMATTER_H
+#define CPPPOINTERFORMATTER_H
+
+#include "cpptools_global.h"
+
+#include <AST.h>
+#include <ASTVisitor.h>
+#include <Overview.h>
+#include <Symbols.h>
+
+#include <cpptools/cpprefactoringchanges.h>
+#include <utils/changeset.h>
+
+namespace CppTools {
+
+using namespace CPlusPlus;
+using namespace CppTools;
+using Utils::ChangeSet;
+
+typedef Utils::ChangeSet::Range Range;
+
+/*!
+ \class CppTools::PointerDeclarationFormatter
+
+ \brief Rewrite pointer or reference declarations accordingly to an Overview.
+
+ The following constructs are supported:
+ \list
+ \o Simple declarations
+ \o Parameters and return types of function declarations and definitions
+ \o Control flow statements like if, while, for, foreach
+ \endlist
+*/
+
+class CPPTOOLS_EXPORT PointerDeclarationFormatter: protected ASTVisitor
+{
+public:
+ /*!
+ \enum PointerDeclarationFormatter::CursorHandling
+
+ This simplifies the QuickFix implementation.
+
+ \value RespectCursor
+ Consider the cursor position / selection of the CppRefactoringFile
+ for rejecting edit operation candidates for the resulting ChangeSet.
+ If there is a selection, the range of the edit operation candidate
+ should be inside the selection. If there is no selection, the cursor
+ position should be within the range of the edit operation candidate.
+ \value IgnoreCursor
+ Cursor position or selection of the CppRefactoringFile will
+ _not_ be considered for aborting.
+ */
+ enum CursorHandling { RespectCursor, IgnoreCursor };
+
+ explicit PointerDeclarationFormatter(const CppRefactoringFilePtr refactoringFile,
+ const Overview &overview,
+ CursorHandling cursorHandling = IgnoreCursor);
+
+ /*!
+ Returns a ChangeSet for applying the formatting changes.
+ The ChangeSet is empty if it was not possible to rewrite anything.
+ */
+ ChangeSet format(AST *ast)
+ {
+ if (ast)
+ accept(ast);
+ return m_changeSet;
+ }
+
+protected:
+ bool visit(SimpleDeclarationAST *ast);
+ bool visit(FunctionDefinitionAST *ast);
+ bool visit(ParameterDeclarationAST *ast);
+ bool visit(IfStatementAST *ast);
+ bool visit(WhileStatementAST *ast);
+ bool visit(ForStatementAST *ast);
+ bool visit(ForeachStatementAST *ast);
+
+private:
+ void processIfWhileForStatement(ExpressionAST *expression, Symbol *symbol);
+ void checkAndRewrite(Symbol *symbol, Range range, unsigned charactersToRemove = 0);
+ QString rewriteDeclaration(FullySpecifiedType type, const Name *name) const;
+
+ const CppRefactoringFilePtr m_cppRefactoringFile;
+ const Overview &m_overview;
+ const CursorHandling m_cursorHandling;
+
+ ChangeSet m_changeSet;
+};
+
+} // namespace CppTools
+
+#endif // CPPPOINTERFORMATTER_H
diff --git a/src/plugins/cpptools/cpppointerdeclarationformatter_test.cpp b/src/plugins/cpptools/cpppointerdeclarationformatter_test.cpp
new file mode 100644
index 0000000000..dbffa14c68
--- /dev/null
+++ b/src/plugins/cpptools/cpppointerdeclarationformatter_test.cpp
@@ -0,0 +1,577 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "cpptoolsplugin.h"
+
+#include <AST.h>
+#include <CppDocument.h>
+#include <Symbols.h>
+#include <TranslationUnit.h>
+#include <pp.h>
+
+#include <cpptools/cpppointerdeclarationformatter.h>
+#include <cpptools/cpprefactoringchanges.h>
+#include <cpptools/cpptoolsplugin.h>
+#include <texteditor/plaintexteditor.h>
+#include <utils/changeset.h>
+#include <utils/fileutils.h>
+
+#include <QDebug>
+#include <QDir>
+#include <QTextCursor>
+#include <QTextDocument>
+#include <QtTest>
+
+using namespace CPlusPlus;
+using namespace CppTools;
+using namespace CppTools::Internal;
+
+using Utils::ChangeSet;
+typedef Utils::ChangeSet::Range Range;
+
+Q_DECLARE_METATYPE(Overview)
+
+static QString stripCursor(const QString &source)
+{
+ QString copy(source);
+ const int pos = copy.indexOf(QLatin1Char('@'));
+ if (pos != -1)
+ copy.remove(pos, 1);
+ else
+ qDebug() << Q_FUNC_INFO << "Warning: No cursor marker to strip.";
+ return copy;
+}
+
+struct TestEnvironment
+{
+ QByteArray source;
+ Snapshot snapshot;
+ CppRefactoringFilePtr cppRefactoringFile;
+ TextEditor::BaseTextEditorWidget *editor;
+ Document::Ptr document;
+ QTextDocument *textDocument;
+ TranslationUnit *translationUnit;
+ Environment env;
+
+ TestEnvironment(const QByteArray &source, Document::ParseMode parseMode)
+ {
+ // Find cursor position and remove cursor marker '@'
+ int cursorPosition = 0;
+ QString sourceWithoutCursorMarker = QLatin1String(source);
+ const int pos = sourceWithoutCursorMarker.indexOf(QLatin1Char('@'));
+ if (pos != -1) {
+ sourceWithoutCursorMarker.remove(pos, 1);
+ cursorPosition = pos;
+ }
+
+ // Write source to temprorary file
+ const QString filePath = QDir::tempPath() + QLatin1String("/file.h");
+ document = Document::create(filePath);
+ Utils::FileSaver documentSaver(document->fileName());
+ documentSaver.write(sourceWithoutCursorMarker.toLatin1());
+ documentSaver.finalize();
+
+ // Preprocess source
+ Preprocessor preprocess(0, &env);
+ const QByteArray preprocessedSource = preprocess.run(filePath, sourceWithoutCursorMarker);
+
+ document->setUtf8Source(preprocessedSource);
+ document->parse(parseMode);
+ document->check();
+ translationUnit = document->translationUnit();
+ snapshot.insert(document);
+ editor = new TextEditor::PlainTextEditorWidget(0);
+ QString error;
+ editor->open(&error, document->fileName(), document->fileName());
+
+ // Set cursor position
+ QTextCursor cursor = editor->textCursor();
+ cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, cursorPosition);
+ editor->setTextCursor(cursor);
+
+ textDocument = editor->document();
+ cppRefactoringFile = CppRefactoringChanges::file(editor, document);
+ }
+
+ void applyFormatting(AST *ast, PointerDeclarationFormatter::CursorHandling cursorHandling)
+ {
+ Overview overview;
+ overview.showReturnTypes = true;
+ overview.showArgumentNames = true;
+ overview.starBindFlags = Overview::StarBindFlags(0);
+
+ // Run the formatter
+ PointerDeclarationFormatter formatter(cppRefactoringFile, overview, cursorHandling);
+ ChangeSet change = formatter.format(ast); // ChangeSet may be empty.
+
+ // Apply change
+ QTextCursor cursor(textDocument);
+ change.apply(&cursor);
+ }
+};
+
+void CppToolsPlugin::test_format_pointerdeclaration_in_simpledeclarations()
+{
+ QFETCH(QString, source);
+ QFETCH(QString, reformattedSource);
+
+ TestEnvironment env(source.toLatin1(), Document::ParseDeclaration);
+ AST *ast = env.translationUnit->ast();
+ QVERIFY(ast);
+
+ env.applyFormatting(ast, PointerDeclarationFormatter::RespectCursor);
+
+ QCOMPARE(env.textDocument->toPlainText(), reformattedSource);
+}
+
+void CppToolsPlugin::test_format_pointerdeclaration_in_simpledeclarations_data()
+{
+ QTest::addColumn<QString>("source");
+ QTest::addColumn<QString>("reformattedSource");
+
+ QString source;
+
+ //
+ // Naming scheme for the test cases: <description>_(in|out)-(start|middle|end)
+ // in/out:
+ // Denotes whether the cursor is in or out of the quickfix activation range
+ // start/middle/end:
+ // Denotes whether is cursor is near to the range start, middle or end
+ //
+
+ QTest::newRow("basic_in-start")
+ << "@char *s;"
+ << "char * s;";
+
+ source = QLatin1String("char *s;@");
+ QTest::newRow("basic_out-end")
+ << source << stripCursor(source);
+
+ QTest::newRow("basic_in-end")
+ << "char *s@;"
+ << "char * s;";
+
+ QTest::newRow("basic-with-ws_in-start")
+ << "\n @char *s; " // Add some whitespace to check position detection.
+ << "\n char * s; ";
+
+ source = QLatin1String("\n @ char *s; ");
+ QTest::newRow("basic-with-ws_out-start")
+ << source << stripCursor(source);
+
+ QTest::newRow("basic-with-const_in-start")
+ << "@const char *s;"
+ << "const char * s;";
+
+ QTest::newRow("basic-with-const-and-init-value_in-end")
+ << "const char *s@ = 0;"
+ << "const char * s = 0;";
+
+ source = QLatin1String("const char *s @= 0;");
+ QTest::newRow("basic-with-const-and-init-value_out-end")
+ << source << stripCursor(source);
+
+ QTest::newRow("first-declarator_in-start")
+ << "@const char *s, *t;"
+ << "const char * s, *t;";
+
+ QTest::newRow("first-declarator_in-end")
+ << "const char *s@, *t;"
+ << "const char * s, *t;";
+
+ source = QLatin1String("const char *s,@ *t;");
+ QTest::newRow("first-declarator_out-end")
+ << source << stripCursor(source);
+
+ QTest::newRow("function-declaration-param_in-start")
+ << "int f(@char *s);"
+ << "int f(char * s);";
+
+ QTest::newRow("function-declaration-param_in-end")
+ << "int f(char *s@);"
+ << "int f(char * s);";
+
+ QTest::newRow("function-declaration-param_in-end")
+ << "int f(char *s@ = 0);"
+ << "int f(char * s = 0);";
+
+ QTest::newRow("function-declaration-param-multiple-params_in-end")
+ << "int f(char *s@, int);"
+ << "int f(char * s, int);";
+
+ source = QLatin1String("int f(char *s)@;");
+ QTest::newRow("function-declaration-param_out-end")
+ << source << stripCursor(source);
+
+ source = QLatin1String("int f@(char *s);");
+ QTest::newRow("function-declaration-param_out-start")
+ << source << stripCursor(source);
+
+ source = QLatin1String("int f(char *s =@ 0);");
+ QTest::newRow("function-declaration-param_out-end")
+ << source << stripCursor(source);
+
+ // Function definitions are handled by the same code as function
+ // declarations, so just check a minimal example.
+ QTest::newRow("function-definition-param_in-middle")
+ << "int f(char @*s) {}"
+ << "int f(char * s) {}";
+
+ QTest::newRow("function-declaration-returntype_in-start")
+ << "@char *f();"
+ << "char * f();";
+
+ QTest::newRow("function-declaration-returntype_in-end")
+ << "char *f@();"
+ << "char * f();";
+
+ source = QLatin1String("char *f(@);");
+ QTest::newRow("function-declaration-returntype_out-end")
+ << source << stripCursor(source);
+
+ QTest::newRow("function-definition-returntype_in-end")
+ << "char *f@() {}"
+ << "char * f() {}";
+
+ QTest::newRow("function-definition-returntype_in-start")
+ << "@char *f() {}"
+ << "char * f() {}";
+
+ source = QLatin1String("char *f(@) {}");
+ QTest::newRow("function-definition-returntype_out-end")
+ << source << stripCursor(source);
+
+ source = QLatin1String("inline@ char *f() {}");
+ QTest::newRow("function-definition-returntype-inline_out-start")
+ << source << stripCursor(source);
+
+ // Same code is also used for for other specifiers like virtual, inline, friend, ...
+ QTest::newRow("function-definition-returntype-static-inline_in-start")
+ << "static inline @char *f() {}"
+ << "static inline char * f() {}";
+
+ QTest::newRow("function-declaration-returntype-static-inline_in-start")
+ << "static inline @char *f();"
+ << "static inline char * f();";
+
+ source = QLatin1String("static inline@ char *f() {}");
+ QTest::newRow("function-definition-returntype-static-inline_out-start")
+ << source << stripCursor(source);
+
+ QTest::newRow("function-declaration-returntype-static-custom-type_in-start")
+ << "static @CustomType *f();"
+ << "static CustomType * f();";
+
+ source = QLatin1String("static@ CustomType *f();");
+ QTest::newRow("function-declaration-returntype-static-custom-type_out-start")
+ << source << stripCursor(source);
+
+ QTest::newRow("function-declaration-returntype-symbolvisibilityattributes_in-start")
+ << "__attribute__((visibility(\"default\"))) @char *f();"
+ << "__attribute__((visibility(\"default\"))) char * f();";
+
+ source = QLatin1String("@__attribute__((visibility(\"default\"))) char *f();");
+ QTest::newRow("function-declaration-returntype-symbolvisibilityattributes_out-start")
+ << source << stripCursor(source);
+
+ // The following two are not supported at the moment.
+ source = QLatin1String("@char * __attribute__((visibility(\"default\"))) f();");
+ QTest::newRow("function-declaration-returntype-symbolvisibilityattributes_out-start")
+ << source << stripCursor(source);
+
+ source = QLatin1String("@char * __attribute__((visibility(\"default\"))) f() {}");
+ QTest::newRow("function-definition-returntype-symbolvisibilityattributes_out-start")
+ << source << stripCursor(source);
+
+ // NOTE: Since __declspec() is not parsed (but defined to nothing),
+ // we can't detect it properly. Therefore, we fail on code with
+ // FOO_EXPORT macros with __declspec() for pointer return types;
+
+ QTest::newRow("typedef_in-start")
+ << "typedef @char *s;"
+ << "typedef char * s;";
+
+ source = QLatin1String("@typedef char *s;");
+ QTest::newRow("typedef_out-start")
+ << source << stripCursor(source);
+
+ QTest::newRow("static_in-start")
+ << "static @char *s;"
+ << "static char * s;";
+
+ QTest::newRow("pointerToFunction_in-start")
+ << "int (*bar)(@char *s);"
+ << "int (*bar)(char * s);";
+
+ source = QLatin1String("int (@*bar)();");
+ QTest::newRow("pointerToFunction_in-start")
+ << source << stripCursor(source);
+
+ source = QLatin1String("int (@*bar)[] = 0;");
+ QTest::newRow("pointerToArray_in-start")
+ << source << stripCursor(source);
+
+ //
+ // Additional test cases that does not fit into the naming scheme
+ //
+
+ // The following case is a side effect of the reformatting. Though
+ // the pointer type is according to the overview, the double space
+ // before 'char' gets corrected.
+ QTest::newRow("remove-extra-whitespace")
+ << "@const char * s = 0;"
+ << "const char * s = 0;";
+
+ // Nothing to pretty print since no pointer or references are involved.
+ source = QLatin1String("@char bla;"); // Two spaces to get sure nothing is reformatted.
+ QTest::newRow("precondition-fail-no-pointer")
+ << source << stripCursor(source);
+}
+
+void CppToolsPlugin::test_format_pointerdeclaration_in_controlflowstatements()
+{
+ QFETCH(QString, source);
+ QFETCH(QString, reformattedSource);
+
+ TestEnvironment env(source.toLatin1(), Document::ParseStatement);
+ AST *ast = env.translationUnit->ast();
+ QVERIFY(ast);
+
+ env.applyFormatting(ast, PointerDeclarationFormatter::RespectCursor);
+
+ QCOMPARE(env.textDocument->toPlainText(), reformattedSource);
+}
+
+void CppToolsPlugin::test_format_pointerdeclaration_in_controlflowstatements_data()
+{
+ QTest::addColumn<QString>("source");
+ QTest::addColumn<QString>("reformattedSource");
+
+ QString source;
+
+ //
+ // Same naming scheme as in test_format_pointerdeclaration_in_simpledeclarations_data()
+ //
+
+ QTest::newRow("if_in-start")
+ << "if (@char *s = 0);"
+ << "if (char * s = 0);";
+
+ source = QLatin1String("if @(char *s = 0);");
+ QTest::newRow("if_out-start")
+ << source << stripCursor(source);
+
+ QTest::newRow("if_in-end")
+ << "if (char *s@ = 0);"
+ << "if (char * s = 0);";
+
+ source = QLatin1String("if (char *s @= 0);");
+ QTest::newRow("if_out-end")
+ << source << stripCursor(source);
+
+ // if and for statements are handled by the same code, so just
+ // check minimal examples for these
+ QTest::newRow("while")
+ << "while (@char *s = 0);"
+ << "while (char * s = 0);";
+
+ QTest::newRow("for")
+ << "for (;@char *s = 0;);"
+ << "for (;char * s = 0;);";
+
+ // Should also work since it's a simple declaration
+ // for (char *s = 0; true;);
+
+ QTest::newRow("foreach_in-start")
+ << "foreach (@char *s, list);"
+ << "foreach (char * s, list);";
+
+ source = QLatin1String("foreach @(char *s, list);");
+ QTest::newRow("foreach-out-start")
+ << source << stripCursor(source);
+
+ QTest::newRow("foreach_in_end")
+ << "foreach (const char *s@, list);"
+ << "foreach (const char * s, list);";
+
+ source = QLatin1String("foreach (const char *s,@ list);");
+ QTest::newRow("foreach_out_end")
+ << source << stripCursor(source);
+
+ //
+ // Additional test cases that does not fit into the naming scheme
+ //
+
+ source = QLatin1String("@if (char s = 0);"); // Two spaces to get sure nothing is reformatted.
+ QTest::newRow("precondition-fail-no-pointer") << source << stripCursor(source);
+}
+
+void CppToolsPlugin::test_format_pointerdeclaration_multiple_declarators()
+{
+ QFETCH(QString, source);
+ QFETCH(QString, reformattedSource);
+
+ TestEnvironment env(source.toLatin1(), Document::ParseDeclaration);
+ AST *ast = env.translationUnit->ast();
+ QVERIFY(ast);
+
+ env.applyFormatting(ast, PointerDeclarationFormatter::RespectCursor);
+
+ QCOMPARE(env.textDocument->toPlainText(), reformattedSource);
+}
+
+void CppToolsPlugin::test_format_pointerdeclaration_multiple_declarators_data()
+{
+ QTest::addColumn<QString>("source");
+ QTest::addColumn<QString>("reformattedSource");
+
+ QString source;
+
+ QTest::newRow("function-declaration_in-start")
+ << "char *s = 0, @*f(int i) = 0;"
+ << "char *s = 0, * f(int i) = 0;";
+
+ QTest::newRow("non-pointer-before_in-start")
+ << "char c, @*t;"
+ << "char c, * t;";
+
+ QTest::newRow("pointer-before_in-start")
+ << "char *s, @*t;"
+ << "char *s, * t;";
+
+ QTest::newRow("pointer-before_in-end")
+ << "char *s, *t@;"
+ << "char *s, * t;";
+
+ source = QLatin1String("char *s,@ *t;");
+ QTest::newRow("md1-out_start")
+ << source << stripCursor(source);
+
+ source = QLatin1String("char *s, *t;@");
+ QTest::newRow("md1-out_end")
+ << source << stripCursor(source);
+
+ QTest::newRow("non-pointer-after_in-start")
+ << "char c, @*t, d;"
+ << "char c, * t, d;";
+
+ QTest::newRow("pointer-after_in-start")
+ << "char c, @*t, *d;"
+ << "char c, * t, *d;";
+
+ QTest::newRow("function-pointer_in-start")
+ << "char *s, @*(*foo)(char *s) = 0;"
+ << "char *s, *(*foo)(char * s) = 0;";
+}
+
+void CppToolsPlugin::test_format_pointerdeclaration_multiple_matches()
+{
+ QFETCH(QString, source);
+ QFETCH(QString, reformattedSource);
+
+ TestEnvironment env(source.toLatin1(), Document::ParseTranlationUnit);
+ AST *ast = env.translationUnit->ast();
+ QVERIFY(ast);
+
+ env.applyFormatting(ast, PointerDeclarationFormatter::IgnoreCursor);
+
+ QCOMPARE(env.textDocument->toPlainText(), reformattedSource);
+}
+
+void CppToolsPlugin::test_format_pointerdeclaration_multiple_matches_data()
+{
+ QTest::addColumn<QString>("source");
+ QTest::addColumn<QString>("reformattedSource");
+
+ QTest::newRow("rvalue-reference")
+ << "int g(Bar&&c) {}"
+ << "int g(Bar && c) {}";
+
+ QTest::newRow("if2")
+ << "int g() { if (char *s = 0) { char *t = 0; } }"
+ << "int g() { if (char * s = 0) { char * t = 0; } }";
+
+ QTest::newRow("if1")
+ << "int g() { if (int i = 0) { char *t = 0; } }"
+ << "int g() { if (int i = 0) { char * t = 0; } }";
+
+ QTest::newRow("for1")
+ << "int g() { for (char *s = 0; char *t = 0; s++); }"
+ << "int g() { for (char * s = 0; char * t = 0; s++); }";
+
+ QTest::newRow("for2")
+ << "int g() { for (char *s = 0; char *t = 0; s++) { char *u = 0; } }"
+ << "int g() { for (char * s = 0; char * t = 0; s++) { char * u = 0; } }";
+
+ QTest::newRow("for3")
+ << "int g() { for (char *s; char *t = 0; s++) { char *u = 0; } }"
+ << "int g() { for (char * s; char * t = 0; s++) { char * u = 0; } }";
+
+ QTest::newRow("multiple-declarators")
+ << "const char c, *s, *(*foo)(char *s) = 0;"
+ << "const char c, * s, *(*foo)(char * s) = 0;";
+
+ QTest::newRow("complex")
+ <<
+ "int *foo(const int &b, int*, int *&rpi)\n"
+ "{\n"
+ " int*pi = 0;\n"
+ " int*const*const cpcpi = &pi;\n"
+ " int*const*pcpi = &pi;\n"
+ " int**const cppi = &pi;\n"
+ "\n"
+ " void (*foo)(char *s) = 0;\n"
+ " int (*bar)[] = 0;\n"
+ "\n"
+ " char *s = 0, *f(int i) = 0;\n"
+ " const char c, *s, *(*foo)(char *s) = 0;"
+ "\n"
+ " for (char *s; char *t = 0; s++) { char *u = 0; }"
+ "\n"
+ " return 0;\n"
+ "}\n"
+ <<
+ "int * foo(const int & b, int *, int *& rpi)\n"
+ "{\n"
+ " int * pi = 0;\n"
+ " int * const * const cpcpi = &pi;\n"
+ " int * const * pcpi = &pi;\n"
+ " int ** const cppi = &pi;\n"
+ "\n"
+ " void (*foo)(char * s) = 0;\n"
+ " int (*bar)[] = 0;\n"
+ "\n"
+ " char * s = 0, * f(int i) = 0;\n"
+ " const char c, * s, *(*foo)(char * s) = 0;"
+ "\n"
+ " for (char * s; char * t = 0; s++) { char * u = 0; }"
+ "\n"
+ " return 0;\n"
+ "}\n";
+}
diff --git a/src/plugins/cpptools/cpptools.pro b/src/plugins/cpptools/cpptools.pro
index 4151ba9b0b..50ffa5d562 100644
--- a/src/plugins/cpptools/cpptools.pro
+++ b/src/plugins/cpptools/cpptools.pro
@@ -47,7 +47,8 @@ HEADERS += completionsettingspage.h \
ModelManagerInterface.h \
TypeHierarchyBuilder.h \
cppindexingsupport.h \
- builtinindexingsupport.h
+ builtinindexingsupport.h \
+ cpppointerdeclarationformatter.h
SOURCES += completionsettingspage.cpp \
cppclassesfilter.cpp \
@@ -88,7 +89,8 @@ SOURCES += completionsettingspage.cpp \
ModelManagerInterface.cpp \
TypeHierarchyBuilder.cpp \
cppindexingsupport.cpp \
- builtinindexingsupport.cpp
+ builtinindexingsupport.cpp \
+ cpppointerdeclarationformatter.cpp
FORMS += completionsettingspage.ui \
cppfilesettingspage.ui \
@@ -99,7 +101,8 @@ equals(TEST, 1) {
cppcodegen_test.cpp \
cppcompletion_test.cpp \
cppmodelmanager_test.cpp \
- modelmanagertesthelper.cpp
+ modelmanagertesthelper.cpp \
+ cpppointerdeclarationformatter_test.cpp
HEADERS += \
modelmanagertesthelper.h
diff --git a/src/plugins/cpptools/cpptools.qbs b/src/plugins/cpptools/cpptools.qbs
index 687182bec9..dc02251a1f 100644
--- a/src/plugins/cpptools/cpptools.qbs
+++ b/src/plugins/cpptools/cpptools.qbs
@@ -74,6 +74,8 @@ QtcPlugin {
"cppmodelmanager.h",
"cppqtstyleindenter.cpp",
"cppqtstyleindenter.h",
+ "cpppointerdeclarationformatter.cpp",
+ "cpppointerdeclarationformatter.h",
"cpprefactoringchanges.cpp",
"cpprefactoringchanges.h",
"cppsemanticinfo.cpp",
@@ -110,7 +112,8 @@ QtcPlugin {
"cppcodegen_test.cpp",
"cppcompletion_test.cpp",
"cppmodelmanager_test.cpp",
- "modelmanagertesthelper.cpp", "modelmanagertesthelper.h"
+ "modelmanagertesthelper.cpp", "modelmanagertesthelper.h",
+ "cpppointerdeclarationformatter_test.cpp"
]
cpp.defines: outer.concat(['SRCDIR="' + FileInfo.path(filePath) + '"'])
diff --git a/src/plugins/cpptools/cpptoolsplugin.h b/src/plugins/cpptools/cpptoolsplugin.h
index 903213d26f..41a7ac4cac 100644
--- a/src/plugins/cpptools/cpptoolsplugin.h
+++ b/src/plugins/cpptools/cpptoolsplugin.h
@@ -74,9 +74,8 @@ public:
private slots:
void switchHeaderSource();
-#ifdef WITH_TESTS
- // codegen tests
+#ifdef WITH_TESTS
void test_codegen_public_in_empty_class();
void test_codegen_public_in_nonempty_class();
void test_codegen_public_before_protected();
@@ -114,6 +113,15 @@ private slots:
void test_completion_instantiate_nested_class_when_enclosing_is_template();
void test_completion_instantiate_nested_of_nested_class_when_enclosing_is_template();
+ void test_format_pointerdeclaration_in_simpledeclarations();
+ void test_format_pointerdeclaration_in_simpledeclarations_data();
+ void test_format_pointerdeclaration_in_controlflowstatements();
+ void test_format_pointerdeclaration_in_controlflowstatements_data();
+ void test_format_pointerdeclaration_multiple_declarators();
+ void test_format_pointerdeclaration_multiple_declarators_data();
+ void test_format_pointerdeclaration_multiple_matches();
+ void test_format_pointerdeclaration_multiple_matches_data();
+
void test_modelmanager_paths();
void test_modelmanager_framework_headers();