cppquickfixes.cpp 215 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

36 37
#include <coreplugin/icore.h>

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

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

53
#include <extensionsystem/pluginmanager.h>
54

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

58 59
#include <utils/qtcassert.h>

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

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

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

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);
124
    plugIn->addAutoReleasedObject(new ExtractLiteralAsParameter);
Nikolai Kosjar's avatar
Nikolai Kosjar committed
125 126 127
    plugIn->addAutoReleasedObject(new GenerateGetterSetter);
    plugIn->addAutoReleasedObject(new InsertDeclFromDef);
    plugIn->addAutoReleasedObject(new InsertDefFromDecl);
128 129 130

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

    plugIn->addAutoReleasedObject(new AssignToLocalVariable);
133 134

    plugIn->addAutoReleasedObject(new InsertVirtualMethods);
135 136

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

139 140 141 142
// In the following anonymous namespace all functions are collected, which could be of interest for
// different quick fixes.
namespace {

143 144 145 146 147 148
enum DefPos {
    DefPosInsideClass,
    DefPosOutsideClass,
    DefPosImplementationFile
};

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

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

165 166
    // ...failed,
    // if class member try to get position right after class
167
    CppRefactoringFilePtr file = refactoring.file(fileName);
168 169 170 171 172 173 174 175 176 177 178 179 180
    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
181 182 183 184 185 186 187 188 189 190
    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);
}

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

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

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

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

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

214
    if (!functionName->isQualifiedNameId())
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
        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;
}

231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
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;
}

269 270 271 272 273
// 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;
274
    LineForNewIncludeDirective finder(file->document(), file->cppDocument()->resolvedIncludes(),
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
                                      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();
}

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

306 307 308 309
} // anonymous namespace

namespace {

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

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

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

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
341 342 343 344 345 346 347 348 349 350 351 352 353 354
    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(")"));
355
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
356 357 358 359
        changes.replace(currentFile->range(binary->binary_op_token), replacement);
        currentFile->setChangeSet(changes);
        currentFile->apply();
    }
360

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

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

369 370
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
371 372 373 374 375 376 377 378
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();
379
    if (!binary)
Nikolai Kosjar's avatar
Nikolai Kosjar committed
380
        return;
381
    if (!interface->isCursorOn(binary->binary_op_token))
Nikolai Kosjar's avatar
Nikolai Kosjar committed
382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406
        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;
    }
407

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

412 413
namespace {

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

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

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

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

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

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

454 455
} // anonymous namespace

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

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490
    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;
    }
491

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

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

503 504
namespace {

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

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540
    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();
    }
};
541

542 543
} // anonymous namespace

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

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

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

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

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
565 566 567 568
    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)) {
569 570
        op->setDescription(QApplication::translate("CppTools::QuickFix",
                                                   "Rewrite Condition Using ||"));
Nikolai Kosjar's avatar
Nikolai Kosjar committed
571 572 573 574
        op->setPriority(index);
        result.append(op);
    }
}
575

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

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

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

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

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

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

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

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

603 604
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
605 606 607 608 609 610 611 612 613 614
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"));
615 616
    }

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

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
624 625 626 627
        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);
628

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

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

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

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

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

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

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

655 656
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
657 658 659 660 661 662 663
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();
664

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

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

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

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
684 685 686 687 688 689
                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;
                }
690 691
            }

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

697 698
namespace {

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

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

        ChangeSet changes;
715

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

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

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

private:
    StatementAST *_statement;
};

731 732
} // anonymous namespace

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
737 738 739 740
    // 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
741
        && !ifStatement->statement->asCompoundStatement()) {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
742 743 744 745 746 747 748 749
        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) {
750
        IfStatementAST *ifStatement = path.at(index)->asIfStatement();
Nikolai Kosjar's avatar
Nikolai Kosjar committed
751 752
        if (ifStatement && ifStatement->statement
            && interface->isCursorOn(ifStatement->statement)
753
            && !ifStatement->statement->asCompoundStatement()) {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
754 755
            result.append(QuickFixOperation::Ptr(
                new AddBracesToIfOp(interface, index, ifStatement->statement)));
756
            return;
757
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
758
    }
759

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

764 765
namespace {

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

775 776 777 778 779
        reset();
    }

    void reset()
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
780 781
        condition = mk.Condition();
        pattern = mk.IfStatement(condition);
782 783
    }

Nikolai Kosjar's avatar
Nikolai Kosjar committed
784
    void perform()
785
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
786 787
        CppRefactoringChanges refactoring(snapshot());
        CppRefactoringFilePtr currentFile = refactoring.file(fileName());
788

Nikolai Kosjar's avatar
Nikolai Kosjar committed
789
        ChangeSet changes;
790

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
793 794 795
        int insertPos = currentFile->startOf(pattern);
        changes.move(currentFile->range(condition), insertPos);
        changes.insert(insertPos, QLatin1String(";\n"));
796

Nikolai Kosjar's avatar
Nikolai Kosjar committed
797 798 799 800
        currentFile->setChangeSet(changes);
        currentFile->appendIndentRange(currentFile->range(pattern));
        currentFile->apply();
    }
801

Nikolai Kosjar's avatar
Nikolai Kosjar committed
802 803 804 805 806
    ASTMatcher matcher;
    ASTPatternBuilder mk;
    ConditionAST *condition;
    IfStatementAST *pattern;
    CoreDeclaratorAST *core;
807 808
};

809 810
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
811 812
void MoveDeclarationOutOfIf::match(const CppQuickFixInterface &interface,
                                   QuickFixOperations &result)
813
{
Nikolai Kosjar's avatar
Nikolai Kosjar committed
814 815 816 817 818 819 820 821 822 823
    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;
824
                if (!op->core)
Nikolai Kosjar's avatar
Nikolai Kosjar committed
825
                    return;
826

Nikolai Kosjar's avatar
Nikolai Kosjar committed
827 828 829 830
                if (interface->isCursorOn(op->core)) {
                    op->setPriority(index);
                    result.append(op);
                    return;
831
                }
832 833

                op->reset();
834 835 836
            }
        }
    }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
837
}
838

839 840
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
841 842 843 844 845
class MoveDeclarationOutOfWhileOp: public CppQuickFixOperation
{
public:
    MoveDeclarationOutOfWhileOp(const CppQuickFixInterface &interface)
        : CppQuickFixOperation(interface)
846
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
847 848
        setDescription(QApplication::translate("CppTools::QuickFix",
                                               "Move Declaration out of Condition"));
849 850
        reset();
    }
851

852 853
    void reset()
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
854 855 856
        condition = mk.Condition();
        pattern = mk.WhileStatement(condition);
    }
857

Nikolai Kosjar's avatar
Nikolai Kosjar committed
858 859 860 861
    void perform()
    {
        CppRefactoringChanges refactoring(snapshot());
        CppRefactoringFilePtr currentFile = refactoring.file(fileName());
862

Nikolai Kosjar's avatar
Nikolai Kosjar committed
863
        ChangeSet changes;
864

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
868 869 870 871 872
        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"));
873

Nikolai Kosjar's avatar
Nikolai Kosjar committed
874 875 876 877
        currentFile->setChangeSet(changes);
        currentFile->appendIndentRange(currentFile->range(pattern));
        currentFile->apply();
    }
878

Nikolai Kosjar's avatar
Nikolai Kosjar committed
879 880 881 882 883 884
    ASTMatcher matcher;
    ASTPatternBuilder mk;
    ConditionAST *condition;
    WhileStatementAST *pattern;
    CoreDeclaratorAST *core;
};
885

886 887
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
888 889
void MoveDeclarationOutOfWhile::match(const CppQuickFixInterface &interface,
                                      QuickFixOperations &result)
890
{
Nikolai Kosjar's avatar
Nikolai Kosjar committed
891 892
    const QList<AST *> &path = interface->path();
    QSharedPointer<MoveDeclarationOutOfWhileOp> op(new MoveDeclarationOutOfWhileOp(interface));
893

Nikolai Kosjar's avatar
Nikolai Kosjar committed
894 895 896 897 898 899
    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;
900

901
                if (!op->core)
Nikolai Kosjar's avatar
Nikolai Kosjar committed
902
                    return;
903

904
                if (!declarator->equal_token)
Nikolai Kosjar's avatar
Nikolai Kosjar committed
905
                    return;
906

907
                if (!declarator->initializer)
Nikolai Kosjar's avatar
Nikolai Kosjar committed
908
                    return;
909

Nikolai Kosjar's avatar
Nikolai Kosjar committed
910 911 912 913
                if (interface->isCursorOn(op->core)) {
                    op->setPriority(index);
                    result.append(op);
                    return;
914
                }
915 916

                op->reset();
917 918 919
            }
        }
    }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
920
}
921

922 923
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
924 925