cppquickfixes.cpp 194 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
2
**
3
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
7
**
hjk's avatar
hjk committed
8 9 10 11 12 13 14
** 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.
15 16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17 18 19 20 21 22 23 24 25
** 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
con's avatar
con committed
26 27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
29

Nikolai Kosjar's avatar
Nikolai Kosjar committed
30 31
#include "cppquickfixes.h"

32
#include "cppeditor.h"
33
#include "cppfunctiondecldeflink.h"
Leandro Melo's avatar
Leandro Melo committed
34
#include "cppquickfixassistant.h"
35

36 37
#include <coreplugin/icore.h>

38
#include <cpptools/cppclassesfilter.h>
39
#include <cpptools/cppcodestylesettings.h>
40 41
#include <cpptools/cpppointerdeclarationformatter.h>
#include <cpptools/cpptoolsconstants.h>
42
#include <cpptools/cpptoolsreuse.h>
43
#include <cpptools/includeutils.h>
44
#include <cpptools/insertionpointlocator.h>
45
#include <cpptools/symbolfinder.h>
46

47
#include <cplusplus/ASTPath.h>
48
#include <cplusplus/CPlusPlusForwardDeclarations.h>
49 50 51 52
#include <cplusplus/CppRewriter.h>
#include <cplusplus/DependencyTable.h>
#include <cplusplus/TypeOfExpression.h>

53
#include <extensionsystem/pluginmanager.h>
54

55 56 57
#include <texteditor/fontsettings.h>
#include <texteditor/texteditorsettings.h>

58 59
#include <utils/qtcassert.h>

60
#include <QApplication>
61 62 63
#include <QCheckBox>
#include <QComboBox>
#include <QDialogButtonBox>
64
#include <QDir>
65
#include <QFileInfo>
66 67
#include <QGroupBox>
#include <QHBoxLayout>
68
#include <QInputDialog>
69 70
#include <QItemDelegate>
#include <QLabel>
71
#include <QMessageBox>
72 73 74
#include <QPointer>
#include <QPushButton>
#include <QQueue>
Nikolai Kosjar's avatar
Nikolai Kosjar committed
75
#include <QSharedPointer>
76 77
#include <QSortFilterProxyModel>
#include <QStandardItemModel>
78 79
#include <QTextBlock>
#include <QTextCursor>
80 81
#include <QTreeView>
#include <QVBoxLayout>
82

Nikolai Kosjar's avatar
Nikolai Kosjar committed
83 84
#include <cctype>

85
using namespace CPlusPlus;
86 87
using namespace CppEditor;
using namespace CppEditor::Internal;
88 89 90
using namespace CppTools;
using namespace TextEditor;
using namespace Utils;
Nikolai Kosjar's avatar
Nikolai Kosjar committed
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126

void CppEditor::Internal::registerQuickFixes(ExtensionSystem::IPlugin *plugIn)
{
    plugIn->addAutoReleasedObject(new AddIncludeForUndefinedIdentifier);
    plugIn->addAutoReleasedObject(new AddIncludeForForwardDeclaration);

    plugIn->addAutoReleasedObject(new FlipLogicalOperands);
    plugIn->addAutoReleasedObject(new InverseLogicalComparison);
    plugIn->addAutoReleasedObject(new RewriteLogicalAnd);

    plugIn->addAutoReleasedObject(new ConvertToCamelCase);

    plugIn->addAutoReleasedObject(new ConvertCStringToNSString);
    plugIn->addAutoReleasedObject(new ConvertNumericLiteral);
    plugIn->addAutoReleasedObject(new TranslateStringLiteral);
    plugIn->addAutoReleasedObject(new WrapStringLiteral);

    plugIn->addAutoReleasedObject(new MoveDeclarationOutOfIf);
    plugIn->addAutoReleasedObject(new MoveDeclarationOutOfWhile);

    plugIn->addAutoReleasedObject(new SplitIfStatement);
    plugIn->addAutoReleasedObject(new SplitSimpleDeclaration);

    plugIn->addAutoReleasedObject(new AddLocalDeclaration);
    plugIn->addAutoReleasedObject(new AddBracesToIf);
    plugIn->addAutoReleasedObject(new RearrangeParamDeclarationList);
    plugIn->addAutoReleasedObject(new ReformatPointerDeclaration);

    plugIn->addAutoReleasedObject(new CompleteSwitchCaseStatement);
    plugIn->addAutoReleasedObject(new InsertQtPropertyMembers);

    plugIn->addAutoReleasedObject(new ApplyDeclDefLinkChanges);
    plugIn->addAutoReleasedObject(new ExtractFunction);
    plugIn->addAutoReleasedObject(new GenerateGetterSetter);
    plugIn->addAutoReleasedObject(new InsertDeclFromDef);
    plugIn->addAutoReleasedObject(new InsertDefFromDecl);
127 128 129

    plugIn->addAutoReleasedObject(new MoveFuncDefOutside);
    plugIn->addAutoReleasedObject(new MoveFuncDefToDecl);
130 131

    plugIn->addAutoReleasedObject(new AssignToLocalVariable);
132 133

    plugIn->addAutoReleasedObject(new InsertVirtualMethods);
Nikolai Kosjar's avatar
Nikolai Kosjar committed
134
}
135

136 137 138 139
// In the following anonymous namespace all functions are collected, which could be of interest for
// different quick fixes.
namespace {

140 141 142 143 144 145
enum DefPos {
    DefPosInsideClass,
    DefPosOutsideClass,
    DefPosImplementationFile
};

146
InsertionLocation insertLocationForMethodDefinition(Symbol *symbol, const bool useSymbolFinder,
147 148 149 150 151 152 153
                                                    CppRefactoringChanges& refactoring,
                                                    const QString& fileName)
{
    QTC_ASSERT(symbol, return InsertionLocation());

    // Try to find optimal location
    const InsertionPointLocator locator(refactoring);
154 155
    const QList<InsertionLocation> list
            = locator.methodDefinition(symbol, useSymbolFinder, fileName);
156 157 158 159 160 161 162 163
    for (int i = 0; i < list.count(); ++i) {
        InsertionLocation location = list.at(i);
        if (location.isValid() && location.fileName() == fileName) {
            return location;
            break;
        }
    }

164 165
    // ...failed,
    // if class member try to get position right after class
166
    CppRefactoringFilePtr file = refactoring.file(fileName);
167 168 169 170 171 172 173 174 175 176 177 178 179
    unsigned line = 0, column = 0;
    if (Class *clazz = symbol->enclosingClass()) {
        if (symbol->fileName() == fileName.toUtf8()) {
            file->cppDocument()->translationUnit()->getPosition(clazz->endOffset(), &line, &column);
            if (line != 0) {
                ++column; // Skipping the ";"
                return InsertionLocation(fileName, QLatin1String("\n\n"), QLatin1String(""),
                                         line, column);
            }
        }
    }

    // fall through: position at end of file
180 181 182 183 184 185 186 187 188 189
    const QTextDocument *doc = file->document();
    int pos = qMax(0, doc->characterCount() - 1);

    //TODO watch for matching namespace
    //TODO watch for moc-includes

    file->lineAndColumn(pos, &line, &column);
    return InsertionLocation(fileName, QLatin1String("\n\n"), QLatin1String("\n"), line, column);
}

190
inline bool isQtStringLiteral(const QByteArray &id)
191 192 193 194
{
    return id == "QLatin1String" || id == "QLatin1Literal" || id == "QStringLiteral";
}

195
inline bool isQtStringTranslation(const QByteArray &id)
196 197 198 199
{
    return id == "tr" || id == "trUtf8" || id == "translate" || id == "QT_TRANSLATE_NOOP";
}

200 201 202 203 204
Class *isMemberFunction(const LookupContext &context, Function *function)
{
    QTC_ASSERT(function, return 0);

    Scope *enclosingScope = function->enclosingScope();
205
    while (!(enclosingScope->isNamespace() || enclosingScope->isClass()))
206 207 208 209
        enclosingScope = enclosingScope->enclosingScope();
    QTC_ASSERT(enclosingScope != 0, return 0);

    const Name *functionName = function->name();
210
    if (!functionName)
211 212
        return 0; // anonymous function names are not valid c++

213
    if (!functionName->isQualifiedNameId())
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
        return 0; // trying to add a declaration for a global function

    const QualifiedNameId *q = functionName->asQualifiedNameId();
    if (!q->base())
        return 0;

    if (ClassOrNamespace *binding = context.lookupType(q->base(), enclosingScope)) {
        foreach (Symbol *s, binding->symbols()) {
            if (Class *matchingClass = s->asClass())
                return matchingClass;
        }
    }

    return 0;
}

230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
Namespace *isNamespaceFunction(const LookupContext &context, Function *function)
{
    QTC_ASSERT(function, return 0);
    if (isMemberFunction(context, function))
        return 0;

    Scope *enclosingScope = function->enclosingScope();
    while (!(enclosingScope->isNamespace() || enclosingScope->isClass()))
        enclosingScope = enclosingScope->enclosingScope();
    QTC_ASSERT(enclosingScope != 0, return 0);

    const Name *functionName = function->name();
    if (!functionName)
        return 0; // anonymous function names are not valid c++

    // global namespace
    if (!functionName->isQualifiedNameId()) {
        foreach (Symbol *s, context.globalNamespace()->symbols()) {
            if (Namespace *matchingNamespace = s->asNamespace())
                return matchingNamespace;
        }
        return 0;
    }

    const QualifiedNameId *q = functionName->asQualifiedNameId();
    if (!q->base())
        return 0;

    if (ClassOrNamespace *binding = context.lookupType(q->base(), enclosingScope)) {
        foreach (Symbol *s, binding->symbols()) {
            if (Namespace *matchingNamespace = s->asNamespace())
                return matchingNamespace;
        }
    }

    return 0;
}

268 269 270 271 272
// Given include is e.g. "afile.h" or <afile.h> (quotes/angle brackets included!).
void insertNewIncludeDirective(const QString &include, CppRefactoringFilePtr file)
{
    // Find optimal position
    using namespace IncludeUtils;
273
    LineForNewIncludeDirective finder(file->document(), file->cppDocument()->resolvedIncludes(),
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
                                      LineForNewIncludeDirective::IgnoreMocIncludes,
                                      LineForNewIncludeDirective::AutoDetect);
    unsigned newLinesToPrepend = 0;
    unsigned newLinesToAppend = 0;
    const int insertLine = finder(include, &newLinesToPrepend, &newLinesToAppend);
    QTC_ASSERT(insertLine >= 1, return);
    const int insertPosition = file->position(insertLine, 1);
    QTC_ASSERT(insertPosition >= 0, return);

    // Construct text to insert
    const QString includeLine = QLatin1String("#include ") + include + QLatin1Char('\n');
    QString prependedNewLines, appendedNewLines;
    while (newLinesToAppend--)
        appendedNewLines += QLatin1String("\n");
    while (newLinesToPrepend--)
        prependedNewLines += QLatin1String("\n");
    const QString textToInsert = prependedNewLines + includeLine + appendedNewLines;

    // Insert
    ChangeSet changes;
    changes.insert(insertPosition, textToInsert);
    file->setChangeSet(changes);
    file->apply();
}

299 300 301 302 303 304
bool nameIncludesOperatorName(const Name *name)
{
    return name->isOperatorNameId()
        || (name->isQualifiedNameId() && name->asQualifiedNameId()->name()->isOperatorNameId());
}

305 306 307 308
} // anonymous namespace

namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
309
class InverseLogicalComparisonOp: public CppQuickFixOperation
310 311
{
public:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
312 313 314 315
    InverseLogicalComparisonOp(const CppQuickFixInterface &interface, int priority,
                               BinaryExpressionAST *binary, Kind invertToken)
        : CppQuickFixOperation(interface, priority)
        , binary(binary), nested(0), negation(0)
316
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
317 318 319
        Token tok;
        tok.f.kind = invertToken;
        replacement = QLatin1String(tok.spell());
320

Nikolai Kosjar's avatar
Nikolai Kosjar committed
321 322 323
        // check for enclosing nested expression
        if (priority - 1 >= 0)
            nested = interface->path()[priority - 1]->asNestedExpression();
324

Nikolai Kosjar's avatar
Nikolai Kosjar committed
325 326 327
        // check for ! before parentheses
        if (nested && priority - 2 >= 0) {
            negation = interface->path()[priority - 2]->asUnaryExpression();
328 329
            if (negation
                    && !interface->currentFile()->tokenAt(negation->unary_op_token).is(T_EXCLAIM)) {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
330
                negation = 0;
331
            }
332 333 334
        }
    }

Nikolai Kosjar's avatar
Nikolai Kosjar committed
335
    QString description() const
336
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
337 338
        return QApplication::translate("CppTools::QuickFix", "Rewrite Using %1").arg(replacement);
    }
339

Nikolai Kosjar's avatar
Nikolai Kosjar committed
340 341 342 343 344 345 346 347 348 349 350 351 352 353
    void perform()
    {
        CppRefactoringChanges refactoring(snapshot());
        CppRefactoringFilePtr currentFile = refactoring.file(fileName());

        ChangeSet changes;
        if (negation) {
            // can't remove parentheses since that might break precedence
            changes.remove(currentFile->range(negation->unary_op_token));
        } else if (nested) {
            changes.insert(currentFile->startOf(nested), QLatin1String("!"));
        } else {
            changes.insert(currentFile->startOf(binary), QLatin1String("!("));
            changes.insert(currentFile->endOf(binary), QLatin1String(")"));
354
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
355 356 357 358
        changes.replace(currentFile->range(binary->binary_op_token), replacement);
        currentFile->setChangeSet(changes);
        currentFile->apply();
    }
359

Nikolai Kosjar's avatar
Nikolai Kosjar committed
360 361 362 363
private:
    BinaryExpressionAST *binary;
    NestedExpressionAST *nested;
    UnaryExpressionAST *negation;
364

Nikolai Kosjar's avatar
Nikolai Kosjar committed
365
    QString replacement;
366 367
};

368 369
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
370 371 372 373 374 375 376 377
void InverseLogicalComparison::match(const CppQuickFixInterface &interface,
                                     QuickFixOperations &result)
{
    CppRefactoringFilePtr file = interface->currentFile();

    const QList<AST *> &path = interface->path();
    int index = path.size() - 1;
    BinaryExpressionAST *binary = path.at(index)->asBinaryExpression();
378
    if (!binary)
Nikolai Kosjar's avatar
Nikolai Kosjar committed
379
        return;
380
    if (!interface->isCursorOn(binary->binary_op_token))
Nikolai Kosjar's avatar
Nikolai Kosjar committed
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405
        return;

    Kind invertToken;
    switch (file->tokenAt(binary->binary_op_token).kind()) {
    case T_LESS_EQUAL:
        invertToken = T_GREATER;
        break;
    case T_LESS:
        invertToken = T_GREATER_EQUAL;
        break;
    case T_GREATER:
        invertToken = T_LESS_EQUAL;
        break;
    case T_GREATER_EQUAL:
        invertToken = T_LESS;
        break;
    case T_EQUAL_EQUAL:
        invertToken = T_EXCLAIM_EQUAL;
        break;
    case T_EXCLAIM_EQUAL:
        invertToken = T_EQUAL_EQUAL;
        break;
    default:
        return;
    }
406

Nikolai Kosjar's avatar
Nikolai Kosjar committed
407 408 409
    result.append(CppQuickFixOperation::Ptr(
        new InverseLogicalComparisonOp(interface, index, binary, invertToken)));
}
410

411 412
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
413
class FlipLogicalOperandsOp: public CppQuickFixOperation
414 415
{
public:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
416 417 418 419 420
    FlipLogicalOperandsOp(const CppQuickFixInterface &interface, int priority,
                          BinaryExpressionAST *binary, QString replacement)
        : CppQuickFixOperation(interface)
        , binary(binary)
        , replacement(replacement)
421
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
422 423
        setPriority(priority);
    }
424

Nikolai Kosjar's avatar
Nikolai Kosjar committed
425 426 427 428 429 430 431
    QString description() const
    {
        if (replacement.isEmpty())
            return QApplication::translate("CppTools::QuickFix", "Swap Operands");
        else
            return QApplication::translate("CppTools::QuickFix", "Rewrite Using %1").arg(replacement);
    }
432

Nikolai Kosjar's avatar
Nikolai Kosjar committed
433 434 435 436
    void perform()
    {
        CppRefactoringChanges refactoring(snapshot());
        CppRefactoringFilePtr currentFile = refactoring.file(fileName());
437

Nikolai Kosjar's avatar
Nikolai Kosjar committed
438
        ChangeSet changes;
439 440
        changes.flip(currentFile->range(binary->left_expression),
                     currentFile->range(binary->right_expression));
441
        if (!replacement.isEmpty())
Nikolai Kosjar's avatar
Nikolai Kosjar committed
442
            changes.replace(currentFile->range(binary->binary_op_token), replacement);
443

Nikolai Kosjar's avatar
Nikolai Kosjar committed
444 445
        currentFile->setChangeSet(changes);
        currentFile->apply();
446 447 448
    }

private:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
449 450 451
    BinaryExpressionAST *binary;
    QString replacement;
};
452

453 454
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
455 456 457 458
void FlipLogicalOperands::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
{
    const QList<AST *> &path = interface->path();
    CppRefactoringFilePtr file = interface->currentFile();
459

Nikolai Kosjar's avatar
Nikolai Kosjar committed
460 461
    int index = path.size() - 1;
    BinaryExpressionAST *binary = path.at(index)->asBinaryExpression();
462
    if (!binary)
Nikolai Kosjar's avatar
Nikolai Kosjar committed
463
        return;
464
    if (!interface->isCursorOn(binary->binary_op_token))
Nikolai Kosjar's avatar
Nikolai Kosjar committed
465
        return;
466

Nikolai Kosjar's avatar
Nikolai Kosjar committed
467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489
    Kind flipToken;
    switch (file->tokenAt(binary->binary_op_token).kind()) {
    case T_LESS_EQUAL:
        flipToken = T_GREATER_EQUAL;
        break;
    case T_LESS:
        flipToken = T_GREATER;
        break;
    case T_GREATER:
        flipToken = T_LESS;
        break;
    case T_GREATER_EQUAL:
        flipToken = T_LESS_EQUAL;
        break;
    case T_EQUAL_EQUAL:
    case T_EXCLAIM_EQUAL:
    case T_AMPER_AMPER:
    case T_PIPE_PIPE:
        flipToken = T_EOF_SYMBOL;
        break;
    default:
        return;
    }
490

Nikolai Kosjar's avatar
Nikolai Kosjar committed
491 492 493 494 495 496
    QString replacement;
    if (flipToken != T_EOF_SYMBOL) {
        Token tok;
        tok.f.kind = flipToken;
        replacement = QLatin1String(tok.spell());
    }
497

Nikolai Kosjar's avatar
Nikolai Kosjar committed
498 499 500
    result.append(QuickFixOperation::Ptr(
        new FlipLogicalOperandsOp(interface, index, binary, replacement)));
}
501

502 503
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
504
class RewriteLogicalAndOp: public CppQuickFixOperation
505 506
{
public:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
507 508 509 510 511 512 513 514
    QSharedPointer<ASTPatternBuilder> mk;
    UnaryExpressionAST *left;
    UnaryExpressionAST *right;
    BinaryExpressionAST *pattern;

    RewriteLogicalAndOp(const CppQuickFixInterface &interface)
        : CppQuickFixOperation(interface)
        , mk(new ASTPatternBuilder)
515
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
516 517 518 519
        left = mk->UnaryExpression();
        right = mk->UnaryExpression();
        pattern = mk->BinaryExpression(left, right);
    }
520

Nikolai Kosjar's avatar
Nikolai Kosjar committed
521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539
    void perform()
    {
        CppRefactoringChanges refactoring(snapshot());
        CppRefactoringFilePtr currentFile = refactoring.file(fileName());

        ChangeSet changes;
        changes.replace(currentFile->range(pattern->binary_op_token), QLatin1String("||"));
        changes.remove(currentFile->range(left->unary_op_token));
        changes.remove(currentFile->range(right->unary_op_token));
        const int start = currentFile->startOf(pattern);
        const int end = currentFile->endOf(pattern);
        changes.insert(start, QLatin1String("!("));
        changes.insert(end, QLatin1String(")"));

        currentFile->setChangeSet(changes);
        currentFile->appendIndentRange(currentFile->range(pattern));
        currentFile->apply();
    }
};
540

541 542
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
543 544 545 546 547
void RewriteLogicalAnd::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
{
    BinaryExpressionAST *expression = 0;
    const QList<AST *> &path = interface->path();
    CppRefactoringFilePtr file = interface->currentFile();
548

Nikolai Kosjar's avatar
Nikolai Kosjar committed
549 550 551 552 553
    int index = path.size() - 1;
    for (; index != -1; --index) {
        expression = path.at(index)->asBinaryExpression();
        if (expression)
            break;
554 555
    }

556
    if (!expression)
Nikolai Kosjar's avatar
Nikolai Kosjar committed
557
        return;
558

559
    if (!interface->isCursorOn(expression->binary_op_token))
Nikolai Kosjar's avatar
Nikolai Kosjar committed
560
        return;
561

Nikolai Kosjar's avatar
Nikolai Kosjar committed
562
    QSharedPointer<RewriteLogicalAndOp> op(new RewriteLogicalAndOp(interface));
563

Nikolai Kosjar's avatar
Nikolai Kosjar committed
564 565 566 567
    if (expression->match(op->pattern, &matcher) &&
            file->tokenAt(op->pattern->binary_op_token).is(T_AMPER_AMPER) &&
            file->tokenAt(op->left->unary_op_token).is(T_EXCLAIM) &&
            file->tokenAt(op->right->unary_op_token).is(T_EXCLAIM)) {
568 569
        op->setDescription(QApplication::translate("CppTools::QuickFix",
                                                   "Rewrite Condition Using ||"));
Nikolai Kosjar's avatar
Nikolai Kosjar committed
570 571 572 573
        op->setPriority(index);
        result.append(op);
    }
}
574

Nikolai Kosjar's avatar
Nikolai Kosjar committed
575 576
bool SplitSimpleDeclaration::checkDeclaration(SimpleDeclarationAST *declaration)
{
577
    if (!declaration->semicolon_token)
Nikolai Kosjar's avatar
Nikolai Kosjar committed
578
        return false;
579

580
    if (!declaration->decl_specifier_list)
Nikolai Kosjar's avatar
Nikolai Kosjar committed
581
        return false;
582

Nikolai Kosjar's avatar
Nikolai Kosjar committed
583 584
    for (SpecifierListAST *it = declaration->decl_specifier_list; it; it = it->next) {
        SpecifierAST *specifier = it->value;
585

Nikolai Kosjar's avatar
Nikolai Kosjar committed
586
        if (specifier->asEnumSpecifier() != 0)
587 588
            return false;

Nikolai Kosjar's avatar
Nikolai Kosjar committed
589
        else if (specifier->asClassSpecifier() != 0)
590
            return false;
Nikolai Kosjar's avatar
Nikolai Kosjar committed
591
    }
592

593
    if (!declaration->declarator_list)
Nikolai Kosjar's avatar
Nikolai Kosjar committed
594
        return false;
595

596
    else if (!declaration->declarator_list->next)
Nikolai Kosjar's avatar
Nikolai Kosjar committed
597
        return false;
598

Nikolai Kosjar's avatar
Nikolai Kosjar committed
599 600
    return true;
}
601

602 603
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
604 605 606 607 608 609 610 611 612 613
class SplitSimpleDeclarationOp: public CppQuickFixOperation
{
public:
    SplitSimpleDeclarationOp(const CppQuickFixInterface &interface, int priority,
                             SimpleDeclarationAST *decl)
        : CppQuickFixOperation(interface, priority)
        , declaration(decl)
    {
        setDescription(QApplication::translate("CppTools::QuickFix",
                                               "Split Declaration"));
614 615
    }

Nikolai Kosjar's avatar
Nikolai Kosjar committed
616
    void perform()
617
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
618 619
        CppRefactoringChanges refactoring(snapshot());
        CppRefactoringFilePtr currentFile = refactoring.file(fileName());
620

Nikolai Kosjar's avatar
Nikolai Kosjar committed
621
        ChangeSet changes;
622

Nikolai Kosjar's avatar
Nikolai Kosjar committed
623 624 625 626
        SpecifierListAST *specifiers = declaration->decl_specifier_list;
        int declSpecifiersStart = currentFile->startOf(specifiers->firstToken());
        int declSpecifiersEnd = currentFile->endOf(specifiers->lastToken() - 1);
        int insertPos = currentFile->endOf(declaration->semicolon_token);
627

Nikolai Kosjar's avatar
Nikolai Kosjar committed
628
        DeclaratorAST *prevDeclarator = declaration->declarator_list->value;
629

Nikolai Kosjar's avatar
Nikolai Kosjar committed
630 631
        for (DeclaratorListAST *it = declaration->declarator_list->next; it; it = it->next) {
            DeclaratorAST *declarator = it->value;
632

Nikolai Kosjar's avatar
Nikolai Kosjar committed
633 634 635 636 637
            changes.insert(insertPos, QLatin1String("\n"));
            changes.copy(declSpecifiersStart, declSpecifiersEnd, insertPos);
            changes.insert(insertPos, QLatin1String(" "));
            changes.move(currentFile->range(declarator), insertPos);
            changes.insert(insertPos, QLatin1String(";"));
638

Nikolai Kosjar's avatar
Nikolai Kosjar committed
639 640
            const int prevDeclEnd = currentFile->endOf(prevDeclarator);
            changes.remove(prevDeclEnd, currentFile->startOf(declarator));
641

Nikolai Kosjar's avatar
Nikolai Kosjar committed
642
            prevDeclarator = declarator;
643
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
644 645 646 647

        currentFile->setChangeSet(changes);
        currentFile->appendIndentRange(currentFile->range(declaration));
        currentFile->apply();
648 649 650
    }

private:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
651 652
    SimpleDeclarationAST *declaration;
};
653

654 655
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
656 657 658 659 660 661 662
void SplitSimpleDeclaration::match(const CppQuickFixInterface &interface,
                                   QuickFixOperations &result)
{
    CoreDeclaratorAST *core_declarator = 0;
    const QList<AST *> &path = interface->path();
    CppRefactoringFilePtr file = interface->currentFile();
    const int cursorPosition = file->cursor().selectionStart();
663

Nikolai Kosjar's avatar
Nikolai Kosjar committed
664 665
    for (int index = path.size() - 1; index != -1; --index) {
        AST *node = path.at(index);
666

667
        if (CoreDeclaratorAST *coreDecl = node->asCoreDeclarator()) {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
668
            core_declarator = coreDecl;
669
        } else if (SimpleDeclarationAST *simpleDecl = node->asSimpleDeclaration()) {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
670 671
            if (checkDeclaration(simpleDecl)) {
                SimpleDeclarationAST *declaration = simpleDecl;
672

Nikolai Kosjar's avatar
Nikolai Kosjar committed
673 674
                const int startOfDeclSpecifier = file->startOf(declaration->decl_specifier_list->firstToken());
                const int endOfDeclSpecifier = file->endOf(declaration->decl_specifier_list->lastToken() - 1);
675

Nikolai Kosjar's avatar
Nikolai Kosjar committed
676 677 678 679 680 681
                if (cursorPosition >= startOfDeclSpecifier && cursorPosition <= endOfDeclSpecifier) {
                    // the AST node under cursor is a specifier.
                    result.append(QuickFixOperation::Ptr(
                        new SplitSimpleDeclarationOp(interface, index, declaration)));
                    return;
                }
682

Nikolai Kosjar's avatar
Nikolai Kosjar committed
683 684 685 686 687 688
                if (core_declarator && interface->isCursorOn(core_declarator)) {
                    // got a core-declarator under the text cursor.
                    result.append(QuickFixOperation::Ptr(
                        new SplitSimpleDeclarationOp(interface, index, declaration)));
                    return;
                }
689 690
            }

Nikolai Kosjar's avatar
Nikolai Kosjar committed
691
            return;
692
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
693 694
    }
}
695

696 697
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
698 699 700 701 702 703 704
class AddBracesToIfOp: public CppQuickFixOperation
{
public:
    AddBracesToIfOp(const CppQuickFixInterface &interface, int priority, StatementAST *statement)
        : CppQuickFixOperation(interface, priority)
        , _statement(statement)
    {
705
        setDescription(QApplication::translate("CppTools::QuickFix", "Add Curly Braces"));
Nikolai Kosjar's avatar
Nikolai Kosjar committed
706 707 708 709 710 711 712 713
    }

    void perform()
    {
        CppRefactoringChanges refactoring(snapshot());
        CppRefactoringFilePtr currentFile = refactoring.file(fileName());

        ChangeSet changes;
714

Nikolai Kosjar's avatar
Nikolai Kosjar committed
715 716
        const int start = currentFile->endOf(_statement->firstToken() - 1);
        changes.insert(start, QLatin1String(" {"));
717

Nikolai Kosjar's avatar
Nikolai Kosjar committed
718 719
        const int end = currentFile->endOf(_statement->lastToken() - 1);
        changes.insert(end, QLatin1String("\n}"));
720

Nikolai Kosjar's avatar
Nikolai Kosjar committed
721
        currentFile->setChangeSet(changes);
722
        currentFile->appendIndentRange(ChangeSet::Range(start, end));
Nikolai Kosjar's avatar
Nikolai Kosjar committed
723 724 725 726 727 728 729
        currentFile->apply();
    }

private:
    StatementAST *_statement;
};

730 731
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
732
void AddBracesToIf::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
733
{
Nikolai Kosjar's avatar
Nikolai Kosjar committed
734
    const QList<AST *> &path = interface->path();
735

Nikolai Kosjar's avatar
Nikolai Kosjar committed
736 737 738 739
    // show when we're on the 'if' of an if statement
    int index = path.size() - 1;
    IfStatementAST *ifStatement = path.at(index)->asIfStatement();
    if (ifStatement && interface->isCursorOn(ifStatement->if_token) && ifStatement->statement
740
        && !ifStatement->statement->asCompoundStatement()) {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
741 742 743 744 745 746 747 748
        result.append(QuickFixOperation::Ptr(
            new AddBracesToIfOp(interface, index, ifStatement->statement)));
        return;
    }

    // or if we're on the statement contained in the if
    // ### This may not be such a good idea, consider nested ifs...
    for (; index != -1; --index) {
749
        IfStatementAST *ifStatement = path.at(index)->asIfStatement();
Nikolai Kosjar's avatar
Nikolai Kosjar committed
750 751
        if (ifStatement && ifStatement->statement
            && interface->isCursorOn(ifStatement->statement)
752
            && !ifStatement->statement->asCompoundStatement()) {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
753 754
            result.append(QuickFixOperation::Ptr(
                new AddBracesToIfOp(interface, index, ifStatement->statement)));
755
            return;
756
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
757
    }
758

Nikolai Kosjar's avatar
Nikolai Kosjar committed
759 760 761
    // ### This could very well be extended to the else branch
    // and other nodes entirely.
}
762

763 764
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
765 766 767 768 769 770 771 772 773 774 775
class MoveDeclarationOutOfIfOp: public CppQuickFixOperation
{
public:
    MoveDeclarationOutOfIfOp(const CppQuickFixInterface &interface)
        : CppQuickFixOperation(interface)
    {
        setDescription(QApplication::translate("CppTools::QuickFix",
                                               "Move Declaration out of Condition"));

        condition = mk.Condition();
        pattern = mk.IfStatement(condition);
776 777
    }

Nikolai Kosjar's avatar
Nikolai Kosjar committed
778
    void perform()
779
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
780 781
        CppRefactoringChanges refactoring(snapshot());
        CppRefactoringFilePtr currentFile = refactoring.file(fileName());
782

Nikolai Kosjar's avatar
Nikolai Kosjar committed
783
        ChangeSet changes;
784

Nikolai Kosjar's avatar
Nikolai Kosjar committed
785
        changes.copy(currentFile->range(core), currentFile->startOf(condition));
786

Nikolai Kosjar's avatar
Nikolai Kosjar committed
787 788 789
        int insertPos = currentFile->startOf(pattern);
        changes.move(currentFile->range(condition), insertPos);
        changes.insert(insertPos, QLatin1String(";\n"));
790

Nikolai Kosjar's avatar
Nikolai Kosjar committed
791 792 793 794
        currentFile->setChangeSet(changes);
        currentFile->appendIndentRange(currentFile->range(pattern));
        currentFile->apply();
    }
795

Nikolai Kosjar's avatar
Nikolai Kosjar committed
796 797 798 799 800
    ASTMatcher matcher;
    ASTPatternBuilder mk;
    ConditionAST *condition;
    IfStatementAST *pattern;
    CoreDeclaratorAST *core;
801 802
};

803 804
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
805 806
void MoveDeclarationOutOfIf::match(const CppQuickFixInterface &interface,
                                   QuickFixOperations &result)
807
{
Nikolai Kosjar's avatar
Nikolai Kosjar committed
808 809 810 811 812 813 814 815 816 817
    const QList<AST *> &path = interface->path();
    typedef QSharedPointer<MoveDeclarationOutOfIfOp> Ptr;
    Ptr op(new MoveDeclarationOutOfIfOp(interface));

    int index = path.size() - 1;
    for (; index != -1; --index) {
        if (IfStatementAST *statement = path.at(index)->asIfStatement()) {
            if (statement->match(op->pattern, &op->matcher) && op->condition->declarator) {
                DeclaratorAST *declarator = op->condition->declarator;
                op->core = declarator->core_declarator;
818
                if (!op->core)
Nikolai Kosjar's avatar
Nikolai Kosjar committed
819
                    return;
820

Nikolai Kosjar's avatar
Nikolai Kosjar committed
821 822 823 824
                if (interface->isCursorOn(op->core)) {
                    op->setPriority(index);
                    result.append(op);
                    return;
825 826 827 828
                }
            }
        }
    }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
829
}
830

831 832
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
833 834 835 836 837
class MoveDeclarationOutOfWhileOp: public CppQuickFixOperation
{
public:
    MoveDeclarationOutOfWhileOp(const CppQuickFixInterface &interface)
        : CppQuickFixOperation(interface)
838
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
839 840
        setDescription(QApplication::translate("CppTools::QuickFix",
                                               "Move Declaration out of Condition"));
841

Nikolai Kosjar's avatar
Nikolai Kosjar committed
842 843 844
        condition = mk.Condition();
        pattern = mk.WhileStatement(condition);
    }
845

Nikolai Kosjar's avatar
Nikolai Kosjar committed
846 847 848 849
    void perform()
    {
        CppRefactoringChanges refactoring(snapshot());
        CppRefactoringFilePtr currentFile = refactoring.file(fileName());
850

Nikolai Kosjar's avatar
Nikolai Kosjar committed
851
        ChangeSet changes;
852

Nikolai Kosjar's avatar
Nikolai Kosjar committed
853 854
        changes.insert(currentFile->startOf(condition), QLatin1String("("));
        changes.insert(currentFile->endOf(condition), QLatin1String(") != 0"));
855

Nikolai Kosjar's avatar
Nikolai Kosjar committed
856 857 858 859 860
        int insertPos = currentFile->startOf(pattern);
        const int conditionStart = currentFile->startOf(condition);
        changes.move(conditionStart, currentFile->startOf(core), insertPos);
        changes.copy(currentFile->range(core), insertPos);
        changes.insert(insertPos, QLatin1String(";\n"));
861

Nikolai Kosjar's avatar
Nikolai Kosjar committed
862 863 864 865
        currentFile->setChangeSet(changes);
        currentFile->appendIndentRange(currentFile->range(pattern));
        currentFile->apply();
    }
866

Nikolai Kosjar's avatar
Nikolai Kosjar committed
867 868 869 870 871 872
    ASTMatcher matcher;
    ASTPatternBuilder mk;
    ConditionAST *condition;
    WhileStatementAST *pattern;
    CoreDeclaratorAST *core;
};
873

874 875
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
876 877
void MoveDeclarationOutOfWhile::match(const CppQuickFixInterface &interface,
                                      QuickFixOperations &result)
878
{
Nikolai Kosjar's avatar
Nikolai Kosjar committed
879 880
    const QList<AST *> &path = interface->path();
    QSharedPointer<MoveDeclarationOutOfWhileOp> op(new MoveDeclarationOutOfWhileOp(interface));
881

Nikolai Kosjar's avatar
Nikolai Kosjar committed
882 883 884 885 886 887
    int index = path.size() - 1;
    for (; index != -1; --index) {
        if (WhileStatementAST *statement = path.at(index)->asWhileStatement()) {
            if (statement->match(op->pattern, &op->matcher) && op->condition->declarator) {
                DeclaratorAST *declarator = op->condition->declarator;
                op->core = declarator->core_declarator;
888

889
                if (!op->core)
Nikolai Kosjar's avatar
Nikolai Kosjar committed
890
                    return;
891

892
                if (!declarator->equal_token)
Nikolai Kosjar's avatar
Nikolai Kosjar committed
893
                    return;
894

895
                if (!declarator->initializer)
Nikolai Kosjar's avatar
Nikolai Kosjar committed
896
                    return;
897

Nikolai Kosjar's avatar
Nikolai Kosjar committed
898 899 900 901
                if (interface->isCursorOn(op->core)) {
                    op->setPriority(index);
                    result.append(op);
                    return;
902 903 904 905
                }
            }
        }
    }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
906
}
907

908 909
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
910 911 912 913 914 915 916 917
class SplitIfStatementOp: public CppQuickFixOperation
{
public:
    SplitIfStatementOp(const CppQuickFixInterface &interface, int priority,
                       IfStatementAST *pattern, BinaryExpressionAST *condition)
        : CppQuickFixOperation(interface, priority)
        , pattern(pattern)
        , condition(condition)
918
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
919 920 921 922 923 924 925 926
        setDescription(QApplication::translate("CppTools::QuickFix",
                                               "Split if Statement"));
    }

    void perform()
    {
        CppRefactoringChanges refactoring(snapshot());
        CppRefactoringFilePtr currentFile = refactoring.file(fileName());
927

Nikolai Kosjar's avatar
Nikolai Kosjar committed
928
        const Token binaryToken = currentFile->tokenAt(condition->binary_op_token);
929

Nikolai Kosjar's avatar
Nikolai Kosjar committed
930 931 932 933 934
        if (binaryToken.is(T_AMPER_AMPER))
            splitAndCondition(currentFile);
        else
            splitOrCondition(currentFile);
    }
935

Nikolai Kosjar's avatar
Nikolai Kosjar committed
936 937 938
    void splitAndCondition(CppRefactoringFilePtr currentFile) const
    {
        ChangeSet changes;
939

Nikolai Kosjar's avatar
Nikolai Kosjar committed
940 941 942 943
        int startPos = currentFile->startOf(pattern);
        changes.insert(startPos, QLatin1String("if ("));
        changes.move(currentFile->range(condition->left_expression), startPos);
        changes.insert(startPos, QLatin1String(") {\n"));
944

Nikolai Kosjar's avatar
Nikolai Kosjar committed
945 946 947
        const int lExprEnd = currentFile->endOf(condition->left_expression);
        changes.remove(lExprEnd, currentFile->startOf(condition->right_expression));
        changes.insert(currentFile->endOf(pattern), QLatin1String("\n}"));
948

Nikolai Kosjar's avatar
Nikolai Kosjar committed
949 950 951 952
        currentFile->setChangeSet(changes);
        currentFile->appendIndentRange(currentFile->range(pattern));
        currentFile->apply();
    }
953

Nikolai Kosjar's avatar
Nikolai Kosjar committed
954
    void splitOrCondition(CppRefactoringFilePtr currentFile) const
955
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
956
        ChangeSet changes;
957

Nikolai Kosjar's avatar
Nikolai Kosjar committed
958 959
        StatementAST *ifTrueStatement = pattern->statement;
        CompoundStatementAST *compoundStatement = ifTrueStatement->asCompoundStatement();
960

Nikolai Kosjar's avatar
Nikolai Kosjar committed
961 962 963 964 965 966
        int insertPos = currentFile->endOf(ifTrueStatement);
        if (compoundStatement)
            changes.insert(insertPos, QLatin1String(" "));
        else
            changes.insert(insertPos, QLatin1String("\n"));
        changes.insert(insertPos, QLatin1String("else if ("));
967

Nikolai Kosjar's avatar
Nikolai Kosjar committed
968 969 970
        const int rExprStart = currentFile->startOf(condition->right_expression);
        changes.move(rExprStart, currentFile->startOf(pattern->rparen_token), insertPos);
        changes.insert(insertPos, QLatin1String(")"));
971

Nikolai Kosjar's avatar
Nikolai Kosjar committed
972 973
        const int rParenEnd = currentFile->endOf(pattern->rparen_token);
        changes.copy(rParenEnd, currentFile->endOf(pattern->statement), insertPos);
974

Nikolai Kosjar's avatar
Nikolai Kosjar committed
975 976 977 978 979 980
        const int lExprEnd = currentFile->endOf(condition->left_expression);
        changes.remove(lExprEnd, currentFile->startOf(condition->right_expression));

        currentFile->setChangeSet(changes);
        currentFile->appendIndentRange(currentFile->range(pattern));
        currentFile->apply();
981 982 983
    }

private:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
984 985 986
    IfStatementAST *pattern;
    BinaryExpressionAST *condition;
};
987

988 989
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
990 991 992 993
void SplitIfStatement::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
{
    IfStatementAST *pattern = 0;
    const QList<AST *> &path = interface->path();
994

Nikolai Kosjar's avatar
Nikolai Kosjar committed
995 996 997 998 999 1000
    int index = path.size() - 1;
    for (; index != -1; --index) {
        AST *node = path.at(index);
        if (IfStatementAST *stmt = node->asIfStatement()) {
            pattern = stmt;
            break;
1001
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
1002
    }
1003

1004
    if (!pattern || !pattern->statement)
Nikolai Kosjar's avatar
Nikolai Kosjar committed
1005
        return;
1006

Nikolai Kosjar's avatar
Nikolai Kosjar committed
1007 1008 1009 1010
    unsigned splitKind = 0;
    for (++index; index < path.size(); ++index) {
        AST *node = path.at(index);
        BinaryExpressionAST *condition = node->asBinaryExpression();
1011
        if (!condition)
Nikolai Kosjar's avatar
Nikolai Kosjar committed
1012
            return;
1013

Nikolai Kosjar's avatar
Nikolai Kosjar committed
1014
        Token binaryToken = interface->currentFile()->tokenAt(condition->binary_op_token);
1015

Nikolai Kosjar's avatar
Nikolai Kosjar committed
1016
        // only accept a chain of ||s or &&s - no mixing
1017
        if (!splitKind) {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
1018 1019