cppquickfixes.cpp 210 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
#include <cplusplus/CppRewriter.h>
#include <cplusplus/TypeOfExpression.h>
54
#include <cplusplus/TypePrettyPrinter.h>
55

56
#include <extensionsystem/pluginmanager.h>
57

58 59
#include <utils/qtcassert.h>

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

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

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

77 78 79 80
namespace CppEditor {
namespace Internal {

void registerQuickFixes(ExtensionSystem::IPlugin *plugIn)
Nikolai Kosjar's avatar
Nikolai Kosjar committed
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
{
    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);
109
    plugIn->addAutoReleasedObject(new ConvertQt4Connect);
Nikolai Kosjar's avatar
Nikolai Kosjar committed
110 111

    plugIn->addAutoReleasedObject(new ApplyDeclDefLinkChanges);
112
    plugIn->addAutoReleasedObject(new ConvertFromAndToPointer);
Nikolai Kosjar's avatar
Nikolai Kosjar committed
113
    plugIn->addAutoReleasedObject(new ExtractFunction);
114
    plugIn->addAutoReleasedObject(new ExtractLiteralAsParameter);
Nikolai Kosjar's avatar
Nikolai Kosjar committed
115 116 117
    plugIn->addAutoReleasedObject(new GenerateGetterSetter);
    plugIn->addAutoReleasedObject(new InsertDeclFromDef);
    plugIn->addAutoReleasedObject(new InsertDefFromDecl);
118 119 120

    plugIn->addAutoReleasedObject(new MoveFuncDefOutside);
    plugIn->addAutoReleasedObject(new MoveFuncDefToDecl);
121 122

    plugIn->addAutoReleasedObject(new AssignToLocalVariable);
123 124

    plugIn->addAutoReleasedObject(new InsertVirtualMethods);
125 126

    plugIn->addAutoReleasedObject(new OptimizeForLoop);
127 128

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

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

135 136 137 138 139 140
enum DefPos {
    DefPosInsideClass,
    DefPosOutsideClass,
    DefPosImplementationFile
};

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

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

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

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

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

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

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

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

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

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

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

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

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

298 299 300 301
} // anonymous namespace

namespace {

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

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

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

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

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

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
358
    QString replacement;
359 360
};

361 362
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
363 364 365
void InverseLogicalComparison::match(const CppQuickFixInterface &interface,
                                     QuickFixOperations &result)
{
366
    CppRefactoringFilePtr file = interface.currentFile();
Nikolai Kosjar's avatar
Nikolai Kosjar committed
367

368
    const QList<AST *> &path = interface.path();
Nikolai Kosjar's avatar
Nikolai Kosjar committed
369 370
    int index = path.size() - 1;
    BinaryExpressionAST *binary = path.at(index)->asBinaryExpression();
371
    if (!binary)
Nikolai Kosjar's avatar
Nikolai Kosjar committed
372
        return;
373
    if (!interface.isCursorOn(binary->binary_op_token))
Nikolai Kosjar's avatar
Nikolai Kosjar committed
374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398
        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;
    }
399

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

403 404
namespace {

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

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

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

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

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

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

445 446
} // anonymous namespace

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

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

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

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

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

493 494
namespace {

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

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

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

532 533
} // anonymous namespace

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

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

547
    if (!expression)
Nikolai Kosjar's avatar
Nikolai Kosjar committed
548
        return;
549

550
    if (!interface.isCursorOn(expression->binary_op_token))
Nikolai Kosjar's avatar
Nikolai Kosjar committed
551
        return;
552

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

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

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

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

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

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

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

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

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
590 591
    return true;
}
592

593 594
namespace {

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

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
612
        ChangeSet changes;
613

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

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

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

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

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
633
            prevDeclarator = declarator;
634
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
635 636 637 638

        currentFile->setChangeSet(changes);
        currentFile->appendIndentRange(currentFile->range(declaration));
        currentFile->apply();
639 640 641
    }

private:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
642 643
    SimpleDeclarationAST *declaration;
};
644

645 646
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
647 648 649 650
void SplitSimpleDeclaration::match(const CppQuickFixInterface &interface,
                                   QuickFixOperations &result)
{
    CoreDeclaratorAST *core_declarator = 0;
651 652
    const QList<AST *> &path = interface.path();
    CppRefactoringFilePtr file = interface.currentFile();
Nikolai Kosjar's avatar
Nikolai Kosjar committed
653
    const int cursorPosition = file->cursor().selectionStart();
654

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

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

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

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

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
680
            return;
681
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
682 683
    }
}
684

685 686
namespace {

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

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

        ChangeSet changes;
703

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

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

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

private:
    StatementAST *_statement;
};

719 720
} // anonymous namespace

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
725 726 727
    // show when we're on the 'if' of an if statement
    int index = path.size() - 1;
    IfStatementAST *ifStatement = path.at(index)->asIfStatement();
728
    if (ifStatement && interface.isCursorOn(ifStatement->if_token) && ifStatement->statement
729
        && !ifStatement->statement->asCompoundStatement()) {
730
        result.append(new AddBracesToIfOp(interface, index, ifStatement->statement));
Nikolai Kosjar's avatar
Nikolai Kosjar committed
731 732 733 734 735 736
        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) {
737
        IfStatementAST *ifStatement = path.at(index)->asIfStatement();
Nikolai Kosjar's avatar
Nikolai Kosjar committed
738
        if (ifStatement && ifStatement->statement
739
            && interface.isCursorOn(ifStatement->statement)
740
            && !ifStatement->statement->asCompoundStatement()) {
741
            result.append(new AddBracesToIfOp(interface, index, ifStatement->statement));
742
            return;
743
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
744
    }
745

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

750 751
namespace {

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

761 762 763 764 765
        reset();
    }

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

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
775
        ChangeSet changes;
776

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

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

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

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

795 796
} // anonymous namespace

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

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

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

825 826
namespace {

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

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

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
849
        ChangeSet changes;
850

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

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

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

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

872 873
} // anonymous namespace

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