From 138066792e61925b827d9d722669b8d96bc0a87b Mon Sep 17 00:00:00 2001
From: Nikolai Kosjar <nikolai.kosjar@digia.com>
Date: Mon, 14 Jan 2013 14:45:36 +0100
Subject: [PATCH] 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>
---
 src/libs/3rdparty/cplusplus/Bind.cpp          |   7 +
 src/libs/3rdparty/cplusplus/Bind.h            |   1 +
 src/libs/cplusplus/CppDocument.cpp            |   2 +
 src/libs/cplusplus/Overview.h                 |   3 -
 .../cpppointerdeclarationformatter.cpp        | 417 +++++++++++++
 .../cpptools/cpppointerdeclarationformatter.h | 122 ++++
 .../cpppointerdeclarationformatter_test.cpp   | 577 ++++++++++++++++++
 src/plugins/cpptools/cpptools.pro             |   9 +-
 src/plugins/cpptools/cpptools.qbs             |   5 +-
 src/plugins/cpptools/cpptoolsplugin.h         |  12 +-
 10 files changed, 1146 insertions(+), 9 deletions(-)
 create mode 100644 src/plugins/cpptools/cpppointerdeclarationformatter.cpp
 create mode 100644 src/plugins/cpptools/cpppointerdeclarationformatter.h
 create mode 100644 src/plugins/cpptools/cpppointerdeclarationformatter_test.cpp

diff --git a/src/libs/3rdparty/cplusplus/Bind.cpp b/src/libs/3rdparty/cplusplus/Bind.cpp
index 909ef24e78e..98310f9ee8b 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 93022b9af77..ca2c6084ba1 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 2069dd9dc3e..ed8b0a6699e 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 339a6d8fc7a..405bbb2b461 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 00000000000..ba0fa4c60fa
--- /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 00000000000..214e72396c3
--- /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 00000000000..dbffa14c68e
--- /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 4151ba9b0b9..50ffa5d5626 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 687182bec9c..dc02251a1f2 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 903213d26f5..41a7ac4cac5 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();
 
-- 
GitLab