cppquickfixes.cpp 201 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
2
**
3
** Copyright (C) 2014 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 "cppeditordocument.h"
34
#include "cppfunctiondecldeflink.h"
Leandro Melo's avatar
Leandro Melo committed
35
#include "cppquickfixassistant.h"
36
#include "cppvirtualfunctionassistprovider.h"
37
#include "cppinsertvirtualmethods.h"
38

39 40
#include <coreplugin/icore.h>

41
#include <cpptools/cppclassesfilter.h>
42
#include <cpptools/cppcodestylesettings.h>
43 44
#include <cpptools/cpppointerdeclarationformatter.h>
#include <cpptools/cpptoolsconstants.h>
45
#include <cpptools/cpptoolsreuse.h>
46
#include <cpptools/includeutils.h>
47
#include <cpptools/insertionpointlocator.h>
48
#include <cpptools/symbolfinder.h>
49

50
#include <cplusplus/ASTPath.h>
51
#include <cplusplus/CPlusPlusForwardDeclarations.h>
52 53 54
#include <cplusplus/CppRewriter.h>
#include <cplusplus/TypeOfExpression.h>

55
#include <extensionsystem/pluginmanager.h>
56

57 58
#include <utils/qtcassert.h>

59
#include <QApplication>
60
#include <QDir>
61
#include <QFileInfo>
62 63
#include <QInputDialog>
#include <QMessageBox>
Nikolai Kosjar's avatar
Nikolai Kosjar committed
64
#include <QSharedPointer>
65
#include <QStack>
66
#include <QTextCursor>
67
#include <QTextCodec>
68

Nikolai Kosjar's avatar
Nikolai Kosjar committed
69 70
#include <cctype>

71 72 73
using namespace CPlusPlus;
using namespace CppTools;
using namespace TextEditor;
Orgad Shaneh's avatar
Orgad Shaneh committed
74
using Utils::ChangeSet;
Nikolai Kosjar's avatar
Nikolai Kosjar committed
75

76 77 78 79
namespace CppEditor {
namespace Internal {

void registerQuickFixes(ExtensionSystem::IPlugin *plugIn)
Nikolai Kosjar's avatar
Nikolai Kosjar committed
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
{
    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);
110
    plugIn->addAutoReleasedObject(new ConvertFromAndToPointer);
Nikolai Kosjar's avatar
Nikolai Kosjar committed
111
    plugIn->addAutoReleasedObject(new ExtractFunction);
112
    plugIn->addAutoReleasedObject(new ExtractLiteralAsParameter);
Nikolai Kosjar's avatar
Nikolai Kosjar committed
113 114 115
    plugIn->addAutoReleasedObject(new GenerateGetterSetter);
    plugIn->addAutoReleasedObject(new InsertDeclFromDef);
    plugIn->addAutoReleasedObject(new InsertDefFromDecl);
116 117 118

    plugIn->addAutoReleasedObject(new MoveFuncDefOutside);
    plugIn->addAutoReleasedObject(new MoveFuncDefToDecl);
119 120

    plugIn->addAutoReleasedObject(new AssignToLocalVariable);
121 122

    plugIn->addAutoReleasedObject(new InsertVirtualMethods);
123 124

    plugIn->addAutoReleasedObject(new OptimizeForLoop);
125 126

    plugIn->addAutoReleasedObject(new EscapeStringLiteral);
Nikolai Kosjar's avatar
Nikolai Kosjar committed
127
}
128

129 130 131 132
// In the following anonymous namespace all functions are collected, which could be of interest for
// different quick fixes.
namespace {

133 134 135 136 137 138
enum DefPos {
    DefPosInsideClass,
    DefPosOutsideClass,
    DefPosImplementationFile
};

139
InsertionLocation insertLocationForMethodDefinition(Symbol *symbol, const bool useSymbolFinder,
140 141 142 143 144 145 146
                                                    CppRefactoringChanges& refactoring,
                                                    const QString& fileName)
{
    QTC_ASSERT(symbol, return InsertionLocation());

    // Try to find optimal location
    const InsertionPointLocator locator(refactoring);
147 148
    const QList<InsertionLocation> list
            = locator.methodDefinition(symbol, useSymbolFinder, fileName);
149 150
    for (int i = 0; i < list.count(); ++i) {
        InsertionLocation location = list.at(i);
Orgad Shaneh's avatar
Orgad Shaneh committed
151
        if (location.isValid() && location.fileName() == fileName)
152 153 154
            return location;
    }

155 156
    // ...failed,
    // if class member try to get position right after class
157
    CppRefactoringFilePtr file = refactoring.file(fileName);
158 159 160 161 162 163 164 165 166 167 168 169 170
    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
171 172 173 174 175 176 177 178 179 180
    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);
}

181
inline bool isQtStringLiteral(const QByteArray &id)
182 183 184 185
{
    return id == "QLatin1String" || id == "QLatin1Literal" || id == "QStringLiteral";
}

186
inline bool isQtStringTranslation(const QByteArray &id)
187 188 189 190
{
    return id == "tr" || id == "trUtf8" || id == "translate" || id == "QT_TRANSLATE_NOOP";
}

191 192 193 194 195
Class *isMemberFunction(const LookupContext &context, Function *function)
{
    QTC_ASSERT(function, return 0);

    Scope *enclosingScope = function->enclosingScope();
196
    while (!(enclosingScope->isNamespace() || enclosingScope->isClass()))
197 198 199 200
        enclosingScope = enclosingScope->enclosingScope();
    QTC_ASSERT(enclosingScope != 0, return 0);

    const Name *functionName = function->name();
201
    if (!functionName)
202
        return 0;
203

204
    if (!functionName->isQualifiedNameId())
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
        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;
}

221 222 223 224 225 226 227 228 229 230 231 232 233
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)
234
        return 0;
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258

    // 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;
}

259 260 261 262 263
// 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;
264
    LineForNewIncludeDirective finder(file->document(), file->cppDocument()->resolvedIncludes(),
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
                                      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();
}

290 291 292 293 294 295
bool nameIncludesOperatorName(const Name *name)
{
    return name->isOperatorNameId()
        || (name->isQualifiedNameId() && name->asQualifiedNameId()->name()->isOperatorNameId());
}

296 297 298 299
} // anonymous namespace

namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
300
class InverseLogicalComparisonOp: public CppQuickFixOperation
301 302
{
public:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
303 304 305 306
    InverseLogicalComparisonOp(const CppQuickFixInterface &interface, int priority,
                               BinaryExpressionAST *binary, Kind invertToken)
        : CppQuickFixOperation(interface, priority)
        , binary(binary), nested(0), negation(0)
307
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
308 309 310
        Token tok;
        tok.f.kind = invertToken;
        replacement = QLatin1String(tok.spell());
311

Nikolai Kosjar's avatar
Nikolai Kosjar committed
312 313 314
        // check for enclosing nested expression
        if (priority - 1 >= 0)
            nested = interface->path()[priority - 1]->asNestedExpression();
315

Nikolai Kosjar's avatar
Nikolai Kosjar committed
316 317 318
        // check for ! before parentheses
        if (nested && priority - 2 >= 0) {
            negation = interface->path()[priority - 2]->asUnaryExpression();
319 320
            if (negation
                    && !interface->currentFile()->tokenAt(negation->unary_op_token).is(T_EXCLAIM)) {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
321
                negation = 0;
322
            }
323 324 325
        }
    }

Nikolai Kosjar's avatar
Nikolai Kosjar committed
326
    QString description() const
327
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
328 329
        return QApplication::translate("CppTools::QuickFix", "Rewrite Using %1").arg(replacement);
    }
330

Nikolai Kosjar's avatar
Nikolai Kosjar committed
331 332 333 334 335 336 337 338 339 340 341 342 343 344
    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(")"));
345
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
346 347 348 349
        changes.replace(currentFile->range(binary->binary_op_token), replacement);
        currentFile->setChangeSet(changes);
        currentFile->apply();
    }
350

Nikolai Kosjar's avatar
Nikolai Kosjar committed
351 352 353 354
private:
    BinaryExpressionAST *binary;
    NestedExpressionAST *nested;
    UnaryExpressionAST *negation;
355

Nikolai Kosjar's avatar
Nikolai Kosjar committed
356
    QString replacement;
357 358
};

359 360
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
361 362 363 364 365 366 367 368
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();
369
    if (!binary)
Nikolai Kosjar's avatar
Nikolai Kosjar committed
370
        return;
371
    if (!interface->isCursorOn(binary->binary_op_token))
Nikolai Kosjar's avatar
Nikolai Kosjar committed
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396
        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;
    }
397

398
    result.append(new InverseLogicalComparisonOp(interface, index, binary, invertToken));
Nikolai Kosjar's avatar
Nikolai Kosjar committed
399
}
400

401 402
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
403
class FlipLogicalOperandsOp: public CppQuickFixOperation
404 405
{
public:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
406 407 408 409 410
    FlipLogicalOperandsOp(const CppQuickFixInterface &interface, int priority,
                          BinaryExpressionAST *binary, QString replacement)
        : CppQuickFixOperation(interface)
        , binary(binary)
        , replacement(replacement)
411
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
412 413
        setPriority(priority);
    }
414

Nikolai Kosjar's avatar
Nikolai Kosjar committed
415 416 417 418 419 420 421
    QString description() const
    {
        if (replacement.isEmpty())
            return QApplication::translate("CppTools::QuickFix", "Swap Operands");
        else
            return QApplication::translate("CppTools::QuickFix", "Rewrite Using %1").arg(replacement);
    }
422

Nikolai Kosjar's avatar
Nikolai Kosjar committed
423 424 425 426
    void perform()
    {
        CppRefactoringChanges refactoring(snapshot());
        CppRefactoringFilePtr currentFile = refactoring.file(fileName());
427

Nikolai Kosjar's avatar
Nikolai Kosjar committed
428
        ChangeSet changes;
429 430
        changes.flip(currentFile->range(binary->left_expression),
                     currentFile->range(binary->right_expression));
431
        if (!replacement.isEmpty())
Nikolai Kosjar's avatar
Nikolai Kosjar committed
432
            changes.replace(currentFile->range(binary->binary_op_token), replacement);
433

Nikolai Kosjar's avatar
Nikolai Kosjar committed
434 435
        currentFile->setChangeSet(changes);
        currentFile->apply();
436 437 438
    }

private:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
439 440 441
    BinaryExpressionAST *binary;
    QString replacement;
};
442

443 444
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
445 446 447 448
void FlipLogicalOperands::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
{
    const QList<AST *> &path = interface->path();
    CppRefactoringFilePtr file = interface->currentFile();
449

Nikolai Kosjar's avatar
Nikolai Kosjar committed
450 451
    int index = path.size() - 1;
    BinaryExpressionAST *binary = path.at(index)->asBinaryExpression();
452
    if (!binary)
Nikolai Kosjar's avatar
Nikolai Kosjar committed
453
        return;
454
    if (!interface->isCursorOn(binary->binary_op_token))
Nikolai Kosjar's avatar
Nikolai Kosjar committed
455
        return;
456

Nikolai Kosjar's avatar
Nikolai Kosjar committed
457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479
    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;
    }
480

Nikolai Kosjar's avatar
Nikolai Kosjar committed
481 482 483 484 485 486
    QString replacement;
    if (flipToken != T_EOF_SYMBOL) {
        Token tok;
        tok.f.kind = flipToken;
        replacement = QLatin1String(tok.spell());
    }
487

488
    result.append(new FlipLogicalOperandsOp(interface, index, binary, replacement));
Nikolai Kosjar's avatar
Nikolai Kosjar committed
489
}
490

491 492
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
493
class RewriteLogicalAndOp: public CppQuickFixOperation
494 495
{
public:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
496 497 498 499 500 501 502 503
    QSharedPointer<ASTPatternBuilder> mk;
    UnaryExpressionAST *left;
    UnaryExpressionAST *right;
    BinaryExpressionAST *pattern;

    RewriteLogicalAndOp(const CppQuickFixInterface &interface)
        : CppQuickFixOperation(interface)
        , mk(new ASTPatternBuilder)
504
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
505 506 507 508
        left = mk->UnaryExpression();
        right = mk->UnaryExpression();
        pattern = mk->BinaryExpression(left, right);
    }
509

Nikolai Kosjar's avatar
Nikolai Kosjar committed
510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528
    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();
    }
};
529

530 531
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
532 533 534 535 536
void RewriteLogicalAnd::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
{
    BinaryExpressionAST *expression = 0;
    const QList<AST *> &path = interface->path();
    CppRefactoringFilePtr file = interface->currentFile();
537

Nikolai Kosjar's avatar
Nikolai Kosjar committed
538 539 540 541 542
    int index = path.size() - 1;
    for (; index != -1; --index) {
        expression = path.at(index)->asBinaryExpression();
        if (expression)
            break;
543 544
    }

545
    if (!expression)
Nikolai Kosjar's avatar
Nikolai Kosjar committed
546
        return;
547

548
    if (!interface->isCursorOn(expression->binary_op_token))
Nikolai Kosjar's avatar
Nikolai Kosjar committed
549
        return;
550

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
553 554 555 556
    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)) {
557 558
        op->setDescription(QApplication::translate("CppTools::QuickFix",
                                                   "Rewrite Condition Using ||"));
Nikolai Kosjar's avatar
Nikolai Kosjar committed
559 560 561 562
        op->setPriority(index);
        result.append(op);
    }
}
563

Nikolai Kosjar's avatar
Nikolai Kosjar committed
564 565
bool SplitSimpleDeclaration::checkDeclaration(SimpleDeclarationAST *declaration)
{
566
    if (!declaration->semicolon_token)
Nikolai Kosjar's avatar
Nikolai Kosjar committed
567
        return false;
568

569
    if (!declaration->decl_specifier_list)
Nikolai Kosjar's avatar
Nikolai Kosjar committed
570
        return false;
571

Nikolai Kosjar's avatar
Nikolai Kosjar committed
572 573
    for (SpecifierListAST *it = declaration->decl_specifier_list; it; it = it->next) {
        SpecifierAST *specifier = it->value;
574

Nikolai Kosjar's avatar
Nikolai Kosjar committed
575
        if (specifier->asEnumSpecifier() != 0)
576 577
            return false;

Nikolai Kosjar's avatar
Nikolai Kosjar committed
578
        else if (specifier->asClassSpecifier() != 0)
579
            return false;
Nikolai Kosjar's avatar
Nikolai Kosjar committed
580
    }
581

582
    if (!declaration->declarator_list)
Nikolai Kosjar's avatar
Nikolai Kosjar committed
583
        return false;
584

585
    else if (!declaration->declarator_list->next)
Nikolai Kosjar's avatar
Nikolai Kosjar committed
586
        return false;
587

Nikolai Kosjar's avatar
Nikolai Kosjar committed
588 589
    return true;
}
590

591 592
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
593 594 595 596 597 598 599 600 601 602
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"));
603 604
    }

Nikolai Kosjar's avatar
Nikolai Kosjar committed
605
    void perform()
606
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
607 608
        CppRefactoringChanges refactoring(snapshot());
        CppRefactoringFilePtr currentFile = refactoring.file(fileName());
609

Nikolai Kosjar's avatar
Nikolai Kosjar committed
610
        ChangeSet changes;
611

Nikolai Kosjar's avatar
Nikolai Kosjar committed
612 613 614 615
        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);
616

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
619 620
        for (DeclaratorListAST *it = declaration->declarator_list->next; it; it = it->next) {
            DeclaratorAST *declarator = it->value;
621

Nikolai Kosjar's avatar
Nikolai Kosjar committed
622 623 624 625 626
            changes.insert(insertPos, QLatin1String("\n"));
            changes.copy(declSpecifiersStart, declSpecifiersEnd, insertPos);
            changes.insert(insertPos, QLatin1String(" "));
            changes.move(currentFile->range(declarator), insertPos);
            changes.insert(insertPos, QLatin1String(";"));
627

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
631
            prevDeclarator = declarator;
632
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
633 634 635 636

        currentFile->setChangeSet(changes);
        currentFile->appendIndentRange(currentFile->range(declaration));
        currentFile->apply();
637 638 639
    }

private:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
640 641
    SimpleDeclarationAST *declaration;
};
642

643 644
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
645 646 647 648 649 650 651
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();
652

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

656
        if (CoreDeclaratorAST *coreDecl = node->asCoreDeclarator()) {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
657
            core_declarator = coreDecl;
658
        } else if (SimpleDeclarationAST *simpleDecl = node->asSimpleDeclaration()) {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
659 660
            if (checkDeclaration(simpleDecl)) {
                SimpleDeclarationAST *declaration = simpleDecl;
661

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
665 666
                if (cursorPosition >= startOfDeclSpecifier && cursorPosition <= endOfDeclSpecifier) {
                    // the AST node under cursor is a specifier.
667
                    result.append(new SplitSimpleDeclarationOp(interface, index, declaration));
Nikolai Kosjar's avatar
Nikolai Kosjar committed
668 669
                    return;
                }
670

Nikolai Kosjar's avatar
Nikolai Kosjar committed
671 672
                if (core_declarator && interface->isCursorOn(core_declarator)) {
                    // got a core-declarator under the text cursor.
673
                    result.append(new SplitSimpleDeclarationOp(interface, index, declaration));
Nikolai Kosjar's avatar
Nikolai Kosjar committed
674 675
                    return;
                }
676 677
            }

Nikolai Kosjar's avatar
Nikolai Kosjar committed
678
            return;
679
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
680 681
    }
}
682

683 684
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
685 686 687 688 689 690 691
class AddBracesToIfOp: public CppQuickFixOperation
{
public:
    AddBracesToIfOp(const CppQuickFixInterface &interface, int priority, StatementAST *statement)
        : CppQuickFixOperation(interface, priority)
        , _statement(statement)
    {
692
        setDescription(QApplication::translate("CppTools::QuickFix", "Add Curly Braces"));
Nikolai Kosjar's avatar
Nikolai Kosjar committed
693 694 695 696 697 698 699 700
    }

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

        ChangeSet changes;
701

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

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
708
        currentFile->setChangeSet(changes);
709
        currentFile->appendIndentRange(ChangeSet::Range(start, end));
Nikolai Kosjar's avatar
Nikolai Kosjar committed
710 711 712 713 714 715 716
        currentFile->apply();
    }

private:
    StatementAST *_statement;
};

717 718
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
719
void AddBracesToIf::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
720
{
Nikolai Kosjar's avatar
Nikolai Kosjar committed
721
    const QList<AST *> &path = interface->path();
722

Nikolai Kosjar's avatar
Nikolai Kosjar committed
723 724 725 726
    // 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
727
        && !ifStatement->statement->asCompoundStatement()) {
728
        result.append(new AddBracesToIfOp(interface, index, ifStatement->statement));
Nikolai Kosjar's avatar
Nikolai Kosjar committed
729 730 731 732 733 734
        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) {
735
        IfStatementAST *ifStatement = path.at(index)->asIfStatement();
Nikolai Kosjar's avatar
Nikolai Kosjar committed
736 737
        if (ifStatement && ifStatement->statement
            && interface->isCursorOn(ifStatement->statement)
738
            && !ifStatement->statement->asCompoundStatement()) {
739
            result.append(new AddBracesToIfOp(interface, index, ifStatement->statement));
740
            return;
741
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
742
    }
743

Nikolai Kosjar's avatar
Nikolai Kosjar committed
744 745 746
    // ### This could very well be extended to the else branch
    // and other nodes entirely.
}
747

748 749
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
750 751 752 753 754 755 756 757 758
class MoveDeclarationOutOfIfOp: public CppQuickFixOperation
{
public:
    MoveDeclarationOutOfIfOp(const CppQuickFixInterface &interface)
        : CppQuickFixOperation(interface)
    {
        setDescription(QApplication::translate("CppTools::QuickFix",
                                               "Move Declaration out of Condition"));

759 760 761 762 763
        reset();
    }

    void reset()
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
764 765
        condition = mk.Condition();
        pattern = mk.IfStatement(condition);
766 767
    }

Nikolai Kosjar's avatar
Nikolai Kosjar committed
768
    void perform()
769
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
770 771
        CppRefactoringChanges refactoring(snapshot());
        CppRefactoringFilePtr currentFile = refactoring.file(fileName());
772

Nikolai Kosjar's avatar
Nikolai Kosjar committed
773
        ChangeSet changes;
774

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
777 778 779
        int insertPos = currentFile->startOf(pattern);
        changes.move(currentFile->range(condition), insertPos);
        changes.insert(insertPos, QLatin1String(";\n"));
780

Nikolai Kosjar's avatar
Nikolai Kosjar committed
781 782 783 784
        currentFile->setChangeSet(changes);
        currentFile->appendIndentRange(currentFile->range(pattern));
        currentFile->apply();
    }
785

Nikolai Kosjar's avatar
Nikolai Kosjar committed
786 787 788 789 790
    ASTMatcher matcher;
    ASTPatternBuilder mk;
    ConditionAST *condition;
    IfStatementAST *pattern;
    CoreDeclaratorAST *core;
791 792
};

793 794
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
795 796
void MoveDeclarationOutOfIf::match(const CppQuickFixInterface &interface,
                                   QuickFixOperations &result)
797
{
Nikolai Kosjar's avatar
Nikolai Kosjar committed
798 799 800 801 802 803 804 805 806 807
    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;
808
                if (!op->core)
Nikolai Kosjar's avatar
Nikolai Kosjar committed
809
                    return;
810

Nikolai Kosjar's avatar
Nikolai Kosjar committed
811 812 813 814
                if (interface->isCursorOn(op->core)) {
                    op->setPriority(index);
                    result.append(op);
                    return;
815
                }
816 817

                op->reset();
818 819 820
            }
        }
    }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
821
}
822

823 824
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
825 826 827 828 829
class MoveDeclarationOutOfWhileOp: public CppQuickFixOperation
{
public:
    MoveDeclarationOutOfWhileOp(const CppQuickFixInterface &interface)
        : CppQuickFixOperation(interface)
830
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
831 832
        setDescription(QApplication::translate("CppTools::QuickFix",
                                               "Move Declaration out of Condition"));
833 834
        reset();
    }
835

836 837
    void reset()
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
838 839 840
        condition = mk.Condition();
        pattern = mk.WhileStatement(condition);
    }
841

Nikolai Kosjar's avatar
Nikolai Kosjar committed
842 843 844 845
    void perform()
    {
        CppRefactoringChanges refactoring(snapshot());
        CppRefactoringFilePtr currentFile = refactoring.file(fileName());
846

Nikolai Kosjar's avatar
Nikolai Kosjar committed
847
        ChangeSet changes;
848

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
852 853 854 855 856
        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"));
857

Nikolai Kosjar's avatar
Nikolai Kosjar committed
858 859 860 861
        currentFile->setChangeSet(changes);
        currentFile->appendIndentRange(currentFile->range(pattern));
        currentFile->apply();
    }
862

Nikolai Kosjar's avatar
Nikolai Kosjar committed
863 864 865 866 867 868
    ASTMatcher matcher;
    ASTPatternBuilder mk;
    ConditionAST *condition;
    WhileStatementAST *pattern;
    CoreDeclaratorAST *core;
};
869

870 871
} // anonymous namespace

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
878 879 880 881 882 883
    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;
884

885
                if (!op->core)
Nikolai Kosjar's avatar
Nikolai Kosjar committed
886
                    return;
887

888
                if (!declarator->equal_token)
Nikolai Kosjar's avatar
Nikolai Kosjar committed
889
                    return;
890

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
894 895 896 897