cppquickfixes.cpp 186 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 "cppfunctiondecldeflink.h"
Leandro Melo's avatar
Leandro Melo committed
34
#include "cppquickfixassistant.h"
35
#include "cppvirtualfunctionassistprovider.h"
36
#include "cppinsertvirtualmethods.h"
37

38 39
#include <coreplugin/icore.h>

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

49
#include <cplusplus/ASTPath.h>
50
#include <cplusplus/CPlusPlusForwardDeclarations.h>
51 52 53 54
#include <cplusplus/CppRewriter.h>
#include <cplusplus/DependencyTable.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 <QTextCursor>
66

Nikolai Kosjar's avatar
Nikolai Kosjar committed
67 68
#include <cctype>

69
using namespace CPlusPlus;
70 71
using namespace CppEditor;
using namespace CppEditor::Internal;
72 73
using namespace CppTools;
using namespace TextEditor;
Nikolai Kosjar's avatar
Nikolai Kosjar committed
74 75 76 77 78 79 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

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);
107
    plugIn->addAutoReleasedObject(new ExtractLiteralAsParameter);
Nikolai Kosjar's avatar
Nikolai Kosjar committed
108 109 110
    plugIn->addAutoReleasedObject(new GenerateGetterSetter);
    plugIn->addAutoReleasedObject(new InsertDeclFromDef);
    plugIn->addAutoReleasedObject(new InsertDefFromDecl);
111 112 113

    plugIn->addAutoReleasedObject(new MoveFuncDefOutside);
    plugIn->addAutoReleasedObject(new MoveFuncDefToDecl);
114 115

    plugIn->addAutoReleasedObject(new AssignToLocalVariable);
116 117

    plugIn->addAutoReleasedObject(new InsertVirtualMethods);
118 119

    plugIn->addAutoReleasedObject(new OptimizeForLoop);
Nikolai Kosjar's avatar
Nikolai Kosjar committed
120
}
121

122 123 124 125
// In the following anonymous namespace all functions are collected, which could be of interest for
// different quick fixes.
namespace {

126 127 128 129 130 131
enum DefPos {
    DefPosInsideClass,
    DefPosOutsideClass,
    DefPosImplementationFile
};

132
InsertionLocation insertLocationForMethodDefinition(Symbol *symbol, const bool useSymbolFinder,
133 134 135 136 137 138 139
                                                    CppRefactoringChanges& refactoring,
                                                    const QString& fileName)
{
    QTC_ASSERT(symbol, return InsertionLocation());

    // Try to find optimal location
    const InsertionPointLocator locator(refactoring);
140 141
    const QList<InsertionLocation> list
            = locator.methodDefinition(symbol, useSymbolFinder, fileName);
142 143
    for (int i = 0; i < list.count(); ++i) {
        InsertionLocation location = list.at(i);
Orgad Shaneh's avatar
Orgad Shaneh committed
144
        if (location.isValid() && location.fileName() == fileName)
145 146 147
            return location;
    }

148 149
    // ...failed,
    // if class member try to get position right after class
150
    CppRefactoringFilePtr file = refactoring.file(fileName);
151 152 153 154 155 156 157 158 159 160 161 162 163
    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
164 165 166 167 168 169 170 171 172 173
    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);
}

174
inline bool isQtStringLiteral(const QByteArray &id)
175 176 177 178
{
    return id == "QLatin1String" || id == "QLatin1Literal" || id == "QStringLiteral";
}

179
inline bool isQtStringTranslation(const QByteArray &id)
180 181 182 183
{
    return id == "tr" || id == "trUtf8" || id == "translate" || id == "QT_TRANSLATE_NOOP";
}

184 185 186 187 188
Class *isMemberFunction(const LookupContext &context, Function *function)
{
    QTC_ASSERT(function, return 0);

    Scope *enclosingScope = function->enclosingScope();
189
    while (!(enclosingScope->isNamespace() || enclosingScope->isClass()))
190 191 192 193
        enclosingScope = enclosingScope->enclosingScope();
    QTC_ASSERT(enclosingScope != 0, return 0);

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

197
    if (!functionName->isQualifiedNameId())
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
        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;
}

214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
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;
}

252 253 254 255 256
// 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;
257
    LineForNewIncludeDirective finder(file->document(), file->cppDocument()->resolvedIncludes(),
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282
                                      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();
}

283 284 285 286 287 288
bool nameIncludesOperatorName(const Name *name)
{
    return name->isOperatorNameId()
        || (name->isQualifiedNameId() && name->asQualifiedNameId()->name()->isOperatorNameId());
}

289 290 291 292
} // anonymous namespace

namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
293
class InverseLogicalComparisonOp: public CppQuickFixOperation
294 295
{
public:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
296 297 298 299
    InverseLogicalComparisonOp(const CppQuickFixInterface &interface, int priority,
                               BinaryExpressionAST *binary, Kind invertToken)
        : CppQuickFixOperation(interface, priority)
        , binary(binary), nested(0), negation(0)
300
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
301 302 303
        Token tok;
        tok.f.kind = invertToken;
        replacement = QLatin1String(tok.spell());
304

Nikolai Kosjar's avatar
Nikolai Kosjar committed
305 306 307
        // check for enclosing nested expression
        if (priority - 1 >= 0)
            nested = interface->path()[priority - 1]->asNestedExpression();
308

Nikolai Kosjar's avatar
Nikolai Kosjar committed
309 310 311
        // check for ! before parentheses
        if (nested && priority - 2 >= 0) {
            negation = interface->path()[priority - 2]->asUnaryExpression();
312 313
            if (negation
                    && !interface->currentFile()->tokenAt(negation->unary_op_token).is(T_EXCLAIM)) {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
314
                negation = 0;
315
            }
316 317 318
        }
    }

Nikolai Kosjar's avatar
Nikolai Kosjar committed
319
    QString description() const
320
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
321 322
        return QApplication::translate("CppTools::QuickFix", "Rewrite Using %1").arg(replacement);
    }
323

Nikolai Kosjar's avatar
Nikolai Kosjar committed
324 325 326 327 328 329 330 331 332 333 334 335 336 337
    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(")"));
338
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
339 340 341 342
        changes.replace(currentFile->range(binary->binary_op_token), replacement);
        currentFile->setChangeSet(changes);
        currentFile->apply();
    }
343

Nikolai Kosjar's avatar
Nikolai Kosjar committed
344 345 346 347
private:
    BinaryExpressionAST *binary;
    NestedExpressionAST *nested;
    UnaryExpressionAST *negation;
348

Nikolai Kosjar's avatar
Nikolai Kosjar committed
349
    QString replacement;
350 351
};

352 353
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
354 355 356 357 358 359 360 361
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();
362
    if (!binary)
Nikolai Kosjar's avatar
Nikolai Kosjar committed
363
        return;
364
    if (!interface->isCursorOn(binary->binary_op_token))
Nikolai Kosjar's avatar
Nikolai Kosjar committed
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389
        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;
    }
390

Nikolai Kosjar's avatar
Nikolai Kosjar committed
391 392 393
    result.append(CppQuickFixOperation::Ptr(
        new InverseLogicalComparisonOp(interface, index, binary, invertToken)));
}
394

395 396
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
397
class FlipLogicalOperandsOp: public CppQuickFixOperation
398 399
{
public:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
400 401 402 403 404
    FlipLogicalOperandsOp(const CppQuickFixInterface &interface, int priority,
                          BinaryExpressionAST *binary, QString replacement)
        : CppQuickFixOperation(interface)
        , binary(binary)
        , replacement(replacement)
405
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
406 407
        setPriority(priority);
    }
408

Nikolai Kosjar's avatar
Nikolai Kosjar committed
409 410 411 412 413 414 415
    QString description() const
    {
        if (replacement.isEmpty())
            return QApplication::translate("CppTools::QuickFix", "Swap Operands");
        else
            return QApplication::translate("CppTools::QuickFix", "Rewrite Using %1").arg(replacement);
    }
416

Nikolai Kosjar's avatar
Nikolai Kosjar committed
417 418 419 420
    void perform()
    {
        CppRefactoringChanges refactoring(snapshot());
        CppRefactoringFilePtr currentFile = refactoring.file(fileName());
421

Nikolai Kosjar's avatar
Nikolai Kosjar committed
422
        ChangeSet changes;
423 424
        changes.flip(currentFile->range(binary->left_expression),
                     currentFile->range(binary->right_expression));
425
        if (!replacement.isEmpty())
Nikolai Kosjar's avatar
Nikolai Kosjar committed
426
            changes.replace(currentFile->range(binary->binary_op_token), replacement);
427

Nikolai Kosjar's avatar
Nikolai Kosjar committed
428 429
        currentFile->setChangeSet(changes);
        currentFile->apply();
430 431 432
    }

private:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
433 434 435
    BinaryExpressionAST *binary;
    QString replacement;
};
436

437 438
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
439 440 441 442
void FlipLogicalOperands::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
{
    const QList<AST *> &path = interface->path();
    CppRefactoringFilePtr file = interface->currentFile();
443

Nikolai Kosjar's avatar
Nikolai Kosjar committed
444 445
    int index = path.size() - 1;
    BinaryExpressionAST *binary = path.at(index)->asBinaryExpression();
446
    if (!binary)
Nikolai Kosjar's avatar
Nikolai Kosjar committed
447
        return;
448
    if (!interface->isCursorOn(binary->binary_op_token))
Nikolai Kosjar's avatar
Nikolai Kosjar committed
449
        return;
450

Nikolai Kosjar's avatar
Nikolai Kosjar committed
451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473
    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;
    }
474

Nikolai Kosjar's avatar
Nikolai Kosjar committed
475 476 477 478 479 480
    QString replacement;
    if (flipToken != T_EOF_SYMBOL) {
        Token tok;
        tok.f.kind = flipToken;
        replacement = QLatin1String(tok.spell());
    }
481

Nikolai Kosjar's avatar
Nikolai Kosjar committed
482 483 484
    result.append(QuickFixOperation::Ptr(
        new FlipLogicalOperandsOp(interface, index, binary, replacement)));
}
485

486 487
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
488
class RewriteLogicalAndOp: public CppQuickFixOperation
489 490
{
public:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
491 492 493 494 495 496 497 498
    QSharedPointer<ASTPatternBuilder> mk;
    UnaryExpressionAST *left;
    UnaryExpressionAST *right;
    BinaryExpressionAST *pattern;

    RewriteLogicalAndOp(const CppQuickFixInterface &interface)
        : CppQuickFixOperation(interface)
        , mk(new ASTPatternBuilder)
499
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
500 501 502 503
        left = mk->UnaryExpression();
        right = mk->UnaryExpression();
        pattern = mk->BinaryExpression(left, right);
    }
504

Nikolai Kosjar's avatar
Nikolai Kosjar committed
505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523
    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();
    }
};
524

525 526
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
527 528 529 530 531
void RewriteLogicalAnd::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
{
    BinaryExpressionAST *expression = 0;
    const QList<AST *> &path = interface->path();
    CppRefactoringFilePtr file = interface->currentFile();
532

Nikolai Kosjar's avatar
Nikolai Kosjar committed
533 534 535 536 537
    int index = path.size() - 1;
    for (; index != -1; --index) {
        expression = path.at(index)->asBinaryExpression();
        if (expression)
            break;
538 539
    }

540
    if (!expression)
Nikolai Kosjar's avatar
Nikolai Kosjar committed
541
        return;
542

543
    if (!interface->isCursorOn(expression->binary_op_token))
Nikolai Kosjar's avatar
Nikolai Kosjar committed
544
        return;
545

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

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
559 560
bool SplitSimpleDeclaration::checkDeclaration(SimpleDeclarationAST *declaration)
{
561
    if (!declaration->semicolon_token)
Nikolai Kosjar's avatar
Nikolai Kosjar committed
562
        return false;
563

564
    if (!declaration->decl_specifier_list)
Nikolai Kosjar's avatar
Nikolai Kosjar committed
565
        return false;
566

Nikolai Kosjar's avatar
Nikolai Kosjar committed
567 568
    for (SpecifierListAST *it = declaration->decl_specifier_list; it; it = it->next) {
        SpecifierAST *specifier = it->value;
569

Nikolai Kosjar's avatar
Nikolai Kosjar committed
570
        if (specifier->asEnumSpecifier() != 0)
571 572
            return false;

Nikolai Kosjar's avatar
Nikolai Kosjar committed
573
        else if (specifier->asClassSpecifier() != 0)
574
            return false;
Nikolai Kosjar's avatar
Nikolai Kosjar committed
575
    }
576

577
    if (!declaration->declarator_list)
Nikolai Kosjar's avatar
Nikolai Kosjar committed
578
        return false;
579

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
583 584
    return true;
}
585

586 587
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
588 589 590 591 592 593 594 595 596 597
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"));
598 599
    }

Nikolai Kosjar's avatar
Nikolai Kosjar committed
600
    void perform()
601
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
602 603
        CppRefactoringChanges refactoring(snapshot());
        CppRefactoringFilePtr currentFile = refactoring.file(fileName());
604

Nikolai Kosjar's avatar
Nikolai Kosjar committed
605
        ChangeSet changes;
606

Nikolai Kosjar's avatar
Nikolai Kosjar committed
607 608 609 610
        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);
611

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
614 615
        for (DeclaratorListAST *it = declaration->declarator_list->next; it; it = it->next) {
            DeclaratorAST *declarator = it->value;
616

Nikolai Kosjar's avatar
Nikolai Kosjar committed
617 618 619 620 621
            changes.insert(insertPos, QLatin1String("\n"));
            changes.copy(declSpecifiersStart, declSpecifiersEnd, insertPos);
            changes.insert(insertPos, QLatin1String(" "));
            changes.move(currentFile->range(declarator), insertPos);
            changes.insert(insertPos, QLatin1String(";"));
622

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
626
            prevDeclarator = declarator;
627
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
628 629 630 631

        currentFile->setChangeSet(changes);
        currentFile->appendIndentRange(currentFile->range(declaration));
        currentFile->apply();
632 633 634
    }

private:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
635 636
    SimpleDeclarationAST *declaration;
};
637

638 639
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
640 641 642 643 644 645 646
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();
647

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

651
        if (CoreDeclaratorAST *coreDecl = node->asCoreDeclarator()) {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
652
            core_declarator = coreDecl;
653
        } else if (SimpleDeclarationAST *simpleDecl = node->asSimpleDeclaration()) {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
654 655
            if (checkDeclaration(simpleDecl)) {
                SimpleDeclarationAST *declaration = simpleDecl;
656

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
660 661 662 663 664 665
                if (cursorPosition >= startOfDeclSpecifier && cursorPosition <= endOfDeclSpecifier) {
                    // the AST node under cursor is a specifier.
                    result.append(QuickFixOperation::Ptr(
                        new SplitSimpleDeclarationOp(interface, index, declaration)));
                    return;
                }
666

Nikolai Kosjar's avatar
Nikolai Kosjar committed
667 668 669 670 671 672
                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;
                }
673 674
            }

Nikolai Kosjar's avatar
Nikolai Kosjar committed
675
            return;
676
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
677 678
    }
}
679

680 681
namespace {

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

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

        ChangeSet changes;
698

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

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

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

private:
    StatementAST *_statement;
};

714 715
} // anonymous namespace

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

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

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

747 748
namespace {

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

758 759 760 761 762
        reset();
    }

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

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

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

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

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

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

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

792 793
} // anonymous namespace

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

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

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

822 823
namespace {

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

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

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

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

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

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

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

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

869 870
} // anonymous namespace

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

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

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

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

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
893 894 895 896
                if (interface->isCursorOn(op->core)) {
                    op->setPriority(index);
                    result.append(op);
                    return;
897
                }
898 899

                op->reset();
900 901 902
            }
        }
    }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
903
}
904

905 906
namespace {

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

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