cppquickfixes.cpp 76 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
2
**
hjk's avatar
hjk committed
3 4
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
** 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

30
#include "cppcompleteswitch.h"
31 32
#include "cppeditor.h"
#include "cppquickfix.h"
33
#include "cppinsertdecldef.h"
34
#include "cppinsertqtpropertymembers.h"
Leandro Melo's avatar
Leandro Melo committed
35 36
#include "cppquickfixassistant.h"
#include "cppcompleteswitch.h"
37
#include "cppfunctiondecldeflink.h"
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55

#include <ASTVisitor.h>
#include <AST.h>
#include <ASTMatcher.h>
#include <ASTPatternBuilder.h>
#include <CoreTypes.h>
#include <Literals.h>
#include <Name.h>
#include <Names.h>
#include <Symbol.h>
#include <Symbols.h>
#include <Token.h>
#include <TranslationUnit.h>
#include <Type.h>

#include <cplusplus/DependencyTable.h>
#include <cplusplus/Overview.h>
#include <cplusplus/TypeOfExpression.h>
56
#include <cpptools/ModelManagerInterface.h>
57
#include <cplusplus/CppRewriter.h>
58
#include <cpptools/cppcodestylesettings.h>
59
#include <cpptools/cpptoolsconstants.h>
60
#include <cpptools/cpprefactoringchanges.h>
61
#include <cpptools/insertionpointlocator.h>
62
#include <cpptools/cpptoolsreuse.h>
63 64
#include <cpptools/cppclassesfilter.h>
#include <cpptools/searchsymbols.h>
65
#include <cpptools/symbolfinder.h>
66
#include <extensionsystem/iplugin.h>
67
#include <extensionsystem/pluginmanager.h>
68

69 70
#include <utils/qtcassert.h>

71 72 73 74
#include <QFileInfo>
#include <QApplication>
#include <QTextBlock>
#include <QTextCursor>
75 76
#include <cctype>

77 78
using namespace CppEditor;
using namespace CppEditor::Internal;
79
using namespace CppTools;
80
using namespace CPlusPlus;
hjk's avatar
hjk committed
81
using namespace TextEditor;
82 83
using namespace Utils;

84 85 86 87 88
static inline bool isQtStringLiteral(const QByteArray &id)
{
    return id == "QLatin1String" || id == "QLatin1Literal" || id == "QStringLiteral";
}

89 90 91 92 93
static inline bool isQtStringTranslation(const QByteArray &id)
{
    return id == "tr" || id == "trUtf8" || id == "translate" || id == "QT_TRANSLATE_NOOP";
}

94 95 96 97 98 99 100
namespace {

/*
    Rewrite
    a op b -> !(a invop b)
    (a op b) -> !(a invop b)
    !(a op b) -> (a invob b)
101 102

    Activates on: <= < > >= == !=
103 104 105 106
*/
class UseInverseOp: public CppQuickFixFactory
{
public:
107
    void match(const CppQuickFixInterface &interface, QuickFixOperations &result)
108
    {
109
        CppRefactoringFilePtr file = interface->currentFile();
110

Leandro Melo's avatar
Leandro Melo committed
111
        const QList<AST *> &path = interface->path();
112 113 114
        int index = path.size() - 1;
        BinaryExpressionAST *binary = path.at(index)->asBinaryExpression();
        if (! binary)
115
            return;
Leandro Melo's avatar
Leandro Melo committed
116
        if (! interface->isCursorOn(binary->binary_op_token))
117
            return;
118 119

        Kind invertToken;
120
        switch (file->tokenAt(binary->binary_op_token).kind()) {
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
        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:
140
            return;
141 142
        }

Leandro Melo's avatar
Leandro Melo committed
143
        result.append(CppQuickFixOperation::Ptr(new Operation(interface, index, binary, invertToken)));
144 145 146 147 148 149 150 151 152 153 154 155
    }

private:
    class Operation: public CppQuickFixOperation
    {
        BinaryExpressionAST *binary;
        NestedExpressionAST *nested;
        UnaryExpressionAST *negation;

        QString replacement;

    public:
156
        Operation(const CppQuickFixInterface &interface,
Leandro Melo's avatar
Leandro Melo committed
157 158
            int priority, BinaryExpressionAST *binary, Kind invertToken)
            : CppQuickFixOperation(interface, priority)
159
            , binary(binary), nested(0), negation(0)
160 161 162 163 164 165 166
        {
            Token tok;
            tok.f.kind = invertToken;
            replacement = QLatin1String(tok.spell());

            // check for enclosing nested expression
            if (priority - 1 >= 0)
Leandro Melo's avatar
Leandro Melo committed
167
                nested = interface->path()[priority - 1]->asNestedExpression();
168 169 170

            // check for ! before parentheses
            if (nested && priority - 2 >= 0) {
Leandro Melo's avatar
Leandro Melo committed
171
                negation = interface->path()[priority - 2]->asUnaryExpression();
172
                if (negation && ! interface->currentFile()->tokenAt(negation->unary_op_token).is(T_EXCLAIM))
173 174 175 176 177 178 179 180 181
                    negation = 0;
            }
        }

        virtual QString description() const
        {
            return QApplication::translate("CppTools::QuickFix", "Rewrite Using %1").arg(replacement);
        }

182
        void perform()
183
        {
184 185 186
            CppRefactoringChanges refactoring(snapshot());
            CppRefactoringFilePtr currentFile = refactoring.file(fileName());

187 188 189
            ChangeSet changes;
            if (negation) {
                // can't remove parentheses since that might break precedence
190
                changes.remove(currentFile->range(negation->unary_op_token));
191
            } else if (nested) {
192
                changes.insert(currentFile->startOf(nested), QLatin1String("!"));
193
            } else {
194 195
                changes.insert(currentFile->startOf(binary), QLatin1String("!("));
                changes.insert(currentFile->endOf(binary), QLatin1String(")"));
196
            }
197
            changes.replace(currentFile->range(binary->binary_op_token), replacement);
198 199
            currentFile->setChangeSet(changes);
            currentFile->apply();
200 201 202 203 204 205 206 207 208 209
        }
    };
};

/*
    Rewrite
    a op b

    As
    b flipop a
210 211

    Activates on: <= < > >= == != && ||
212 213 214 215
*/
class FlipBinaryOp: public CppQuickFixFactory
{
public:
216
    void match(const CppQuickFixInterface &interface, QuickFixOperations &result)
217
    {
Leandro Melo's avatar
Leandro Melo committed
218
        const QList<AST *> &path = interface->path();
219
        CppRefactoringFilePtr file = interface->currentFile();
220 221 222 223

        int index = path.size() - 1;
        BinaryExpressionAST *binary = path.at(index)->asBinaryExpression();
        if (! binary)
224
            return;
Leandro Melo's avatar
Leandro Melo committed
225
        if (! interface->isCursorOn(binary->binary_op_token))
226
            return;
227 228

        Kind flipToken;
229
        switch (file->tokenAt(binary->binary_op_token).kind()) {
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
        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:
249
            return;
250 251 252 253 254 255 256 257 258
        }

        QString replacement;
        if (flipToken != T_EOF_SYMBOL) {
            Token tok;
            tok.f.kind = flipToken;
            replacement = QLatin1String(tok.spell());
        }

Leandro Melo's avatar
Leandro Melo committed
259
        result.append(QuickFixOperation::Ptr(new Operation(interface, index, binary, replacement)));
260 261 262 263 264 265
    }

private:
    class Operation: public CppQuickFixOperation
    {
    public:
266
        Operation(const CppQuickFixInterface &interface,
Leandro Melo's avatar
Leandro Melo committed
267 268
                  int priority, BinaryExpressionAST *binary, QString replacement)
            : CppQuickFixOperation(interface)
269 270 271 272 273 274 275 276 277 278 279 280 281 282
            , binary(binary)
            , replacement(replacement)
        {
            setPriority(priority);
        }

        virtual QString description() const
        {
            if (replacement.isEmpty())
                return QApplication::translate("CppTools::QuickFix", "Swap Operands");
            else
                return QApplication::translate("CppTools::QuickFix", "Rewrite Using %1").arg(replacement);
        }

283
        void perform()
284
        {
285 286
            CppRefactoringChanges refactoring(snapshot());
            CppRefactoringFilePtr currentFile = refactoring.file(fileName());
287

288
            ChangeSet changes;
289
            changes.flip(currentFile->range(binary->left_expression), currentFile->range(binary->right_expression));
290
            if (! replacement.isEmpty())
291
                changes.replace(currentFile->range(binary->binary_op_token), replacement);
292

293 294
            currentFile->setChangeSet(changes);
            currentFile->apply();
295 296 297 298 299 300 301 302 303 304 305 306 307 308
        }

    private:
        BinaryExpressionAST *binary;
        QString replacement;
    };
};

/*
    Rewrite
    !a && !b

    As
    !(a || b)
309 310

    Activates on: &&
311 312 313 314
*/
class RewriteLogicalAndOp: public CppQuickFixFactory
{
public:
315
    void match(const CppQuickFixInterface &interface, QuickFixOperations &result)
316 317
    {
        BinaryExpressionAST *expression = 0;
Leandro Melo's avatar
Leandro Melo committed
318
        const QList<AST *> &path = interface->path();
319
        CppRefactoringFilePtr file = interface->currentFile();
320 321 322 323 324 325 326 327 328

        int index = path.size() - 1;
        for (; index != -1; --index) {
            expression = path.at(index)->asBinaryExpression();
            if (expression)
                break;
        }

        if (! expression)
329
            return;
330

Leandro Melo's avatar
Leandro Melo committed
331
        if (! interface->isCursorOn(expression->binary_op_token))
332
            return;
333

Leandro Melo's avatar
Leandro Melo committed
334
        QSharedPointer<Operation> op(new Operation(interface));
335 336

        if (expression->match(op->pattern, &matcher) &&
337 338 339
                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)) {
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354
            op->setDescription(QApplication::translate("CppTools::QuickFix", "Rewrite Condition Using ||"));
            op->setPriority(index);
            result.append(op);
        }
    }

private:
    class Operation: public CppQuickFixOperation
    {
    public:
        QSharedPointer<ASTPatternBuilder> mk;
        UnaryExpressionAST *left;
        UnaryExpressionAST *right;
        BinaryExpressionAST *pattern;

355
        Operation(const CppQuickFixInterface &interface)
Leandro Melo's avatar
Leandro Melo committed
356
            : CppQuickFixOperation(interface)
357 358 359 360 361 362 363
            , mk(new ASTPatternBuilder)
        {
            left = mk->UnaryExpression();
            right = mk->UnaryExpression();
            pattern = mk->BinaryExpression(left, right);
        }

364
        void perform()
365
        {
366 367 368
            CppRefactoringChanges refactoring(snapshot());
            CppRefactoringFilePtr currentFile = refactoring.file(fileName());

369
            ChangeSet changes;
370 371 372 373 374
            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);
375 376 377
            changes.insert(start, QLatin1String("!("));
            changes.insert(end, QLatin1String(")"));

378 379 380
            currentFile->setChangeSet(changes);
            currentFile->appendIndentRange(currentFile->range(pattern));
            currentFile->apply();
381 382 383 384 385 386 387
        }
    };

private:
    ASTMatcher matcher;
};

388 389 390 391 392 393 394
/*
    Rewrite
    int *a, b;

    As
    int *a;
    int b;
395 396

    Activates on: the type or the variable names.
397
*/
398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427
class SplitSimpleDeclarationOp: public CppQuickFixFactory
{
    static bool checkDeclaration(SimpleDeclarationAST *declaration)
    {
        if (! declaration->semicolon_token)
            return false;

        if (! declaration->decl_specifier_list)
            return false;

        for (SpecifierListAST *it = declaration->decl_specifier_list; it; it = it->next) {
            SpecifierAST *specifier = it->value;

            if (specifier->asEnumSpecifier() != 0)
                return false;

            else if (specifier->asClassSpecifier() != 0)
                return false;
        }

        if (! declaration->declarator_list)
            return false;

        else if (! declaration->declarator_list->next)
            return false;

        return true;
    }

public:
428
    void match(const CppQuickFixInterface &interface, QuickFixOperations &result)
429 430
    {
        CoreDeclaratorAST *core_declarator = 0;
Leandro Melo's avatar
Leandro Melo committed
431
        const QList<AST *> &path = interface->path();
432
        CppRefactoringFilePtr file = interface->currentFile();
433
        const int cursorPosition = file->cursor().selectionStart();
434 435 436 437 438 439 440 441 442 443 444

        for (int index = path.size() - 1; index != -1; --index) {
            AST *node = path.at(index);

            if (CoreDeclaratorAST *coreDecl = node->asCoreDeclarator())
                core_declarator = coreDecl;

            else if (SimpleDeclarationAST *simpleDecl = node->asSimpleDeclaration()) {
                if (checkDeclaration(simpleDecl)) {
                    SimpleDeclarationAST *declaration = simpleDecl;

445 446
                    const int startOfDeclSpecifier = file->startOf(declaration->decl_specifier_list->firstToken());
                    const int endOfDeclSpecifier = file->endOf(declaration->decl_specifier_list->lastToken() - 1);
447 448 449

                    if (cursorPosition >= startOfDeclSpecifier && cursorPosition <= endOfDeclSpecifier) {
                        // the AST node under cursor is a specifier.
450 451
                        result.append(QuickFixOperation::Ptr(new Operation(interface, index, declaration)));
                        return;
452 453
                    }

Leandro Melo's avatar
Leandro Melo committed
454
                    if (core_declarator && interface->isCursorOn(core_declarator)) {
455
                        // got a core-declarator under the text cursor.
456 457
                        result.append(QuickFixOperation::Ptr(new Operation(interface, index, declaration)));
                        return;
458 459 460
                    }
                }

461
                return;
462 463 464 465 466 467 468 469
            }
        }
    }

private:
    class Operation: public CppQuickFixOperation
    {
    public:
470
        Operation(const CppQuickFixInterface &interface, int priority, SimpleDeclarationAST *decl)
Leandro Melo's avatar
Leandro Melo committed
471
            : CppQuickFixOperation(interface, priority)
472 473 474 475 476 477
            , declaration(decl)
        {
            setDescription(QApplication::translate("CppTools::QuickFix",
                                                   "Split Declaration"));
        }

478
        void perform()
479
        {
480 481 482
            CppRefactoringChanges refactoring(snapshot());
            CppRefactoringFilePtr currentFile = refactoring.file(fileName());

483 484 485
            ChangeSet changes;

            SpecifierListAST *specifiers = declaration->decl_specifier_list;
486 487 488
            int declSpecifiersStart = currentFile->startOf(specifiers->firstToken());
            int declSpecifiersEnd = currentFile->endOf(specifiers->lastToken() - 1);
            int insertPos = currentFile->endOf(declaration->semicolon_token);
489 490 491 492 493 494 495 496 497

            DeclaratorAST *prevDeclarator = declaration->declarator_list->value;

            for (DeclaratorListAST *it = declaration->declarator_list->next; it; it = it->next) {
                DeclaratorAST *declarator = it->value;

                changes.insert(insertPos, QLatin1String("\n"));
                changes.copy(declSpecifiersStart, declSpecifiersEnd, insertPos);
                changes.insert(insertPos, QLatin1String(" "));
498
                changes.move(currentFile->range(declarator), insertPos);
499 500
                changes.insert(insertPos, QLatin1String(";"));

501 502
                const int prevDeclEnd = currentFile->endOf(prevDeclarator);
                changes.remove(prevDeclEnd, currentFile->startOf(declarator));
503 504 505 506

                prevDeclarator = declarator;
            }

507 508 509
            currentFile->setChangeSet(changes);
            currentFile->appendIndentRange(currentFile->range(declaration));
            currentFile->apply();
510 511 512 513 514 515 516 517 518
        }

    private:
        SimpleDeclarationAST *declaration;
    };
};

/*
    Add curly braces to a if statement that doesn't already contain a
519 520 521 522 523
    compound statement. I.e.

    if (a)
        b;
    becomes
524
    if (a)
525
        b;
526 527

    Activates on: the if
528 529 530 531
*/
class AddBracesToIfOp: public CppQuickFixFactory
{
public:
532
    void match(const CppQuickFixInterface &interface, QuickFixOperations &result)
533
    {
Leandro Melo's avatar
Leandro Melo committed
534
        const QList<AST *> &path = interface->path();
535 536 537 538

        // show when we're on the 'if' of an if statement
        int index = path.size() - 1;
        IfStatementAST *ifStatement = path.at(index)->asIfStatement();
Leandro Melo's avatar
Leandro Melo committed
539
        if (ifStatement && interface->isCursorOn(ifStatement->if_token) && ifStatement->statement
540
            && ! ifStatement->statement->asCompoundStatement()) {
541 542
            result.append(QuickFixOperation::Ptr(new Operation(interface, index, ifStatement->statement)));
            return;
543 544 545 546 547 548 549
        }

        // 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) {
            IfStatementAST *ifStatement = path.at(index)->asIfStatement();
            if (ifStatement && ifStatement->statement
Leandro Melo's avatar
Leandro Melo committed
550
                && interface->isCursorOn(ifStatement->statement)
551
                && ! ifStatement->statement->asCompoundStatement()) {
552 553
                result.append(QuickFixOperation::Ptr(new Operation(interface, index, ifStatement->statement)));
                return;
554 555 556 557 558 559 560 561 562 563 564
            }
        }

        // ### This could very well be extended to the else branch
        // and other nodes entirely.
    }

private:
    class Operation: public CppQuickFixOperation
    {
    public:
565
        Operation(const CppQuickFixInterface &interface, int priority, StatementAST *statement)
Leandro Melo's avatar
Leandro Melo committed
566
            : CppQuickFixOperation(interface, priority)
567 568 569 570 571 572
            , _statement(statement)
        {
            setDescription(QApplication::translate("CppTools::QuickFix",
                                                   "Add Curly Braces"));
        }

573
        void perform()
574
        {
575 576 577
            CppRefactoringChanges refactoring(snapshot());
            CppRefactoringFilePtr currentFile = refactoring.file(fileName());

578 579
            ChangeSet changes;

580
            const int start = currentFile->endOf(_statement->firstToken() - 1);
581 582
            changes.insert(start, QLatin1String(" {"));

583
            const int end = currentFile->endOf(_statement->lastToken() - 1);
584
            changes.insert(end, QLatin1String("\n}"));
585

586 587 588
            currentFile->setChangeSet(changes);
            currentFile->appendIndentRange(Utils::ChangeSet::Range(start, end));
            currentFile->apply();
589 590 591 592 593 594 595 596 597 598 599 600 601 602
        }

    private:
        StatementAST *_statement;
    };
};

/*
    Replace
    if (Type name = foo()) {...}

    With
    Type name = foo;
    if (name) {...}
603 604

    Activates on: the name of the introduced variable
605 606 607 608
*/
class MoveDeclarationOutOfIfOp: public CppQuickFixFactory
{
public:
609
    void match(const CppQuickFixInterface &interface, QuickFixOperations &result)
610
    {
Leandro Melo's avatar
Leandro Melo committed
611 612
        const QList<AST *> &path = interface->path();
        QSharedPointer<Operation> op(new Operation(interface));
613 614 615 616 617 618 619 620

        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;
                    if (! op->core)
621
                        return;
622

Leandro Melo's avatar
Leandro Melo committed
623
                    if (interface->isCursorOn(op->core)) {
624 625
                        op->setPriority(index);
                        result.append(op);
626
                        return;
627 628 629 630 631 632 633 634 635 636
                    }
                }
            }
        }
    }

private:
    class Operation: public CppQuickFixOperation
    {
    public:
637
        Operation(const CppQuickFixInterface &interface)
Leandro Melo's avatar
Leandro Melo committed
638
            : CppQuickFixOperation(interface)
639 640 641 642 643 644 645 646
        {
            setDescription(QApplication::translate("CppTools::QuickFix",
                                                   "Move Declaration out of Condition"));

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

647
        void perform()
648
        {
649 650 651
            CppRefactoringChanges refactoring(snapshot());
            CppRefactoringFilePtr currentFile = refactoring.file(fileName());

652 653
            ChangeSet changes;

654
            changes.copy(currentFile->range(core), currentFile->startOf(condition));
655

656 657
            int insertPos = currentFile->startOf(pattern);
            changes.move(currentFile->range(condition), insertPos);
658 659
            changes.insert(insertPos, QLatin1String(";\n"));

660 661 662
            currentFile->setChangeSet(changes);
            currentFile->appendIndentRange(currentFile->range(pattern));
            currentFile->apply();
663 664 665 666
        }

        ASTMatcher matcher;
        ASTPatternBuilder mk;
667
        CPPEditorWidget *editor;
668 669 670 671 672 673 674 675 676 677 678 679 680
        ConditionAST *condition;
        IfStatementAST *pattern;
        CoreDeclaratorAST *core;
    };
};

/*
    Replace
    while (Type name = foo()) {...}

    With
    Type name;
    while ((name = foo()) != 0) {...}
681 682

    Activates on: the name of the introduced variable
683 684 685 686
*/
class MoveDeclarationOutOfWhileOp: public CppQuickFixFactory
{
public:
687
    void match(const CppQuickFixInterface &interface, QuickFixOperations &result)
688
    {
Leandro Melo's avatar
Leandro Melo committed
689 690
        const QList<AST *> &path = interface->path();
        QSharedPointer<Operation> op(new Operation(interface));
691 692 693 694 695 696 697 698 699

        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;

                    if (! op->core)
700
                        return;
701

702 703
                    if (! declarator->equal_token)
                        return;
704

705 706
                    if (! declarator->initializer)
                        return;
707

Leandro Melo's avatar
Leandro Melo committed
708
                    if (interface->isCursorOn(op->core)) {
709 710
                        op->setPriority(index);
                        result.append(op);
711
                        return;
712 713 714 715 716 717 718 719 720 721
                    }
                }
            }
        }
    }

private:
    class Operation: public CppQuickFixOperation
    {
    public:
722
        Operation(const CppQuickFixInterface &interface)
Leandro Melo's avatar
Leandro Melo committed
723
            : CppQuickFixOperation(interface)
724 725 726 727 728 729 730 731
        {
            setDescription(QApplication::translate("CppTools::QuickFix",
                                                   "Move Declaration out of Condition"));

            condition = mk.Condition();
            pattern = mk.WhileStatement(condition);
        }

732
        void perform()
733
        {
734 735 736
            CppRefactoringChanges refactoring(snapshot());
            CppRefactoringFilePtr currentFile = refactoring.file(fileName());

737 738
            ChangeSet changes;

739 740
            changes.insert(currentFile->startOf(condition), QLatin1String("("));
            changes.insert(currentFile->endOf(condition), QLatin1String(") != 0"));
741

742 743 744 745
            int insertPos = currentFile->startOf(pattern);
            const int conditionStart = currentFile->startOf(condition);
            changes.move(conditionStart, currentFile->startOf(core), insertPos);
            changes.copy(currentFile->range(core), insertPos);
746 747
            changes.insert(insertPos, QLatin1String(";\n"));

748 749 750
            currentFile->setChangeSet(changes);
            currentFile->appendIndentRange(currentFile->range(pattern));
            currentFile->apply();
751 752 753 754
        }

        ASTMatcher matcher;
        ASTPatternBuilder mk;
755
        CPPEditorWidget *editor;
756 757 758 759 760 761 762 763 764 765 766 767
        ConditionAST *condition;
        WhileStatementAST *pattern;
        CoreDeclaratorAST *core;
    };
};

/*
  Replace
     if (something && something_else) {
     }

  with
768 769
     if (something)
        if (something_else)
770 771 772 773 774 775 776 777 778 779 780
     }

  and
    if (something || something_else)
      x;

  with
    if (something)
      x;
    else if (something_else)
      x;
781 782

    Activates on: && or ||
783 784 785 786
*/
class SplitIfStatementOp: public CppQuickFixFactory
{
public:
787
    void match(const CppQuickFixInterface &interface, QuickFixOperations &result)
788 789
    {
        IfStatementAST *pattern = 0;
Leandro Melo's avatar
Leandro Melo committed
790
        const QList<AST *> &path = interface->path();
791 792 793 794 795 796 797 798 799 800 801

        int index = path.size() - 1;
        for (; index != -1; --index) {
            AST *node = path.at(index);
            if (IfStatementAST *stmt = node->asIfStatement()) {
                pattern = stmt;
                break;
            }
        }

        if (! pattern || ! pattern->statement)
802
            return;
803 804 805 806 807 808

        unsigned splitKind = 0;
        for (++index; index < path.size(); ++index) {
            AST *node = path.at(index);
            BinaryExpressionAST *condition = node->asBinaryExpression();
            if (! condition)
809
                return;
810

811
            Token binaryToken = interface->currentFile()->tokenAt(condition->binary_op_token);
812 813 814 815 816

            // only accept a chain of ||s or &&s - no mixing
            if (! splitKind) {
                splitKind = binaryToken.kind();
                if (splitKind != T_AMPER_AMPER && splitKind != T_PIPE_PIPE)
817
                    return;
818 819
                // we can't reliably split &&s in ifs with an else branch
                if (splitKind == T_AMPER_AMPER && pattern->else_statement)
820
                    return;
821
            } else if (splitKind != binaryToken.kind()) {
822
                return;
823 824
            }

825 826 827 828
            if (interface->isCursorOn(condition->binary_op_token)) {
                result.append(QuickFixOperation::Ptr(new Operation(interface, index, pattern, condition)));
                return;
            }
829 830 831 832 833 834 835
        }
    }

private:
    class Operation: public CppQuickFixOperation
    {
    public:
836
        Operation(const CppQuickFixInterface &interface, int priority,
837
                  IfStatementAST *pattern, BinaryExpressionAST *condition)
Leandro Melo's avatar
Leandro Melo committed
838
            : CppQuickFixOperation(interface, priority)
839 840 841 842 843 844 845
            , pattern(pattern)
            , condition(condition)
        {
            setDescription(QApplication::translate("CppTools::QuickFix",
                                                   "Split if Statement"));
        }

846
        void perform()
847
        {
848 849 850
            CppRefactoringChanges refactoring(snapshot());
            CppRefactoringFilePtr currentFile = refactoring.file(fileName());

851
            const Token binaryToken = currentFile->tokenAt(condition->binary_op_token);
852 853

            if (binaryToken.is(T_AMPER_AMPER))
854
                splitAndCondition(currentFile);
855
            else
856
                splitOrCondition(currentFile);
857 858
        }

859
        void splitAndCondition(CppRefactoringFilePtr currentFile)
860 861 862
        {
            ChangeSet changes;

863
            int startPos = currentFile->startOf(pattern);
864
            changes.insert(startPos, QLatin1String("if ("));
865
            changes.move(currentFile->range(condition->left_expression), startPos);
866 867
            changes.insert(startPos, QLatin1String(") {\n"));

868 869 870
            const int lExprEnd = currentFile->endOf(condition->left_expression);
            changes.remove(lExprEnd, currentFile->startOf(condition->right_expression));
            changes.insert(currentFile->endOf(pattern), QLatin1String("\n}"));
871

872 873 874
            currentFile->setChangeSet(changes);
            currentFile->appendIndentRange(currentFile->range(pattern));
            currentFile->apply();
875 876
        }

877
        void splitOrCondition(CppRefactoringFilePtr currentFile)
878 879 880 881 882 883
        {
            ChangeSet changes;

            StatementAST *ifTrueStatement = pattern->statement;
            CompoundStatementAST *compoundStatement = ifTrueStatement->asCompoundStatement();

884
            int insertPos = currentFile->endOf(ifTrueStatement);
885 886 887 888 889 890
            if (compoundStatement)
                changes.insert(insertPos, QLatin1String(" "));
            else
                changes.insert(insertPos, QLatin1String("\n"));
            changes.insert(insertPos, QLatin1String("else if ("));

891 892
            const int rExprStart = currentFile->startOf(condition->right_expression);
            changes.move(rExprStart, currentFile->startOf(pattern->rparen_token), insertPos);
893 894
            changes.insert(insertPos, QLatin1String(")"));

895 896
            const int rParenEnd = currentFile->endOf(pattern->rparen_token);
            changes.copy(rParenEnd, currentFile->endOf(pattern->statement), insertPos);
897

898 899
            const int lExprEnd = currentFile->endOf(condition->left_expression);
            changes.remove(lExprEnd, currentFile->startOf(condition->right_expression));
900

901 902 903
            currentFile->setChangeSet(changes);
            currentFile->appendIndentRange(currentFile->range(pattern));
            currentFile->apply();
904 905 906 907 908 909 910 911 912 913
        }

    private:
        IfStatementAST *pattern;
        BinaryExpressionAST *condition;
    };
};

/*
  Replace
914 915 916 917 918 919
    "abcd"  -> QLatin1String("abcd")
    @"abcd" -> QLatin1String("abcd") (Objective C)
    'a'     -> QLatin1Char('a')
    'a'     -> "a"
    "a"     -> 'a' or QLatin1Char('a') (Single character string constants)
    "\n"    -> '\n', QLatin1Char('\n')
920 921 922
  Except if they are already enclosed in
    QLatin1Char, QT_TRANSLATE_NOOP, tr,
    trUtf8, QLatin1Literal, QLatin1String
923

924
    Activates on: the string or character literal
925
*/
926 927 928 929 930 931 932

static inline QString msgQtStringLiteralDescription(const QString &replacement, int qtVersion)
{
    return QApplication::translate("CppTools::QuickFix", "Enclose in %1(...) (Qt %2)")
           .arg(replacement).arg(qtVersion);
}

933 934 935 936 937
static inline QString msgQtStringLiteralDescription(const QString &replacement)
{
    return QApplication::translate("CppTools::QuickFix", "Enclose in %1(...)").arg(replacement);
}

938 939 940
class WrapStringLiteral: public CppQuickFixFactory
{
public:
941
    typedef const CppQuickFixInterface AssistInterfacePtr;
942 943 944 945 946 947 948 949 950 951 952 953

    enum ActionFlags
    {
        EncloseInQLatin1CharAction = 0x1, EncloseInQLatin1StringAction = 0x2, EncloseInQStringLiteralAction = 0x4,
        EncloseActionMask = EncloseInQLatin1CharAction | EncloseInQLatin1StringAction | EncloseInQStringLiteralAction,
        TranslateTrAction = 0x8, TranslateQCoreApplicationAction = 0x10, TranslateNoopAction = 0x20,
        TranslationMask = TranslateTrAction | TranslateQCoreApplicationAction | TranslateNoopAction,
        RemoveObjectiveCAction = 0x40,
        ConvertEscapeSequencesToCharAction = 0x100, ConvertEscapeSequencesToStringAction = 0x200,
        SingleQuoteAction = 0x400, DoubleQuoteAction = 0x800
    };

954 955
    enum Type { TypeString, TypeObjCString, TypeChar, TypeNone };

956 957
    //void match(const AssistInterfacePtr &interface, QuickFixOperations &result);
    void match(const CppQuickFixInterface &interface, QuickFixOperations &result);
958 959 960 961 962 963 964 965 966 967 968
    static QString replacement(unsigned actions);
    static QByteArray stringToCharEscapeSequences(const QByteArray &content);
    static QByteArray charToStringEscapeSequences(const QByteArray &content);

    static ExpressionAST *analyze(const QList<AST *> &path, const CppRefactoringFilePtr &file,
                                  Type *type,
                                  QByteArray *enclosingFunction = 0,
                                  CallAST **enclosingFunctionCall = 0);

    // Operations performs the operations of type ActionFlags passed in as actions.
    class Operation : public CppQuickFixOperation
969
    {
970 971 972 973
    public:
        Operation(const AssistInterfacePtr &interface, int priority,
                  unsigned actions, const QString &description, ExpressionAST *literal,
                  const QString &translationContext = QString());
974

975 976
        void perform();

977 978 979 980 981 982
    private:
        const unsigned m_actions;
        ExpressionAST *m_literal;
        const QString m_translationContext;
    };
};
983

984 985 986
/* Analze a string/character literal like "x", QLatin1String("x") and return the literal
 * (StringLiteral or NumericLiteral for characters) and its type
 * and the enclosing function (QLatin1String, tr...) */
987

988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012
ExpressionAST *WrapStringLiteral::analyze(const QList<AST *> &path,
                                          const CppRefactoringFilePtr &file,
                                          Type *type,
                                          QByteArray *enclosingFunction /* = 0 */,
                                          CallAST **enclosingFunctionCall /* = 0 */)
{
    *type = TypeNone;
    if (enclosingFunction)
        enclosingFunction->clear();
    if (enclosingFunctionCall)
        *enclosingFunctionCall = 0;

    if (path.isEmpty())
        return 0;

    ExpressionAST *literal = path.last()->asExpression();
    if (literal) {
        if (literal->asStringLiteral()) {
            // Check for Objective C string (@"bla")
            const QChar firstChar = file->charAt(file->startOf(literal));
            *type = firstChar == QLatin1Char('@') ? TypeObjCString : TypeString;
        } else if (NumericLiteralAST *numericLiteral = literal->asNumericLiteral()) {
            // character ('c') constants are numeric.
            if (file->tokenAt(numericLiteral->literal_token).is(T_CHAR_LITERAL))
                *type = TypeChar;
1013
        }
1014
    }
1015

1016 1017 1018 1019 1020 1021 1022 1023
    if (*type != TypeNone && enclosingFunction && path.size() > 1) {
        if (CallAST *call = path.at(path.size() - 2)->asCall()) {
            if (call->base_expression) {
                if (IdExpressionAST *idExpr = call->base_expression->asIdExpression()) {
                    if (SimpleNameAST *functionName = idExpr->name->asSimpleName()) {
                        *enclosingFunction = file->tokenAt(functionName->identifier_token).identifier->chars();
                        if (enclosingFunctionCall)
                            *enclosingFunctionCall = call;
1024 1025 1026 1027
                    }
                }
            }
        }
1028 1029 1030
    }
    return literal;
}
1031

1032
void WrapStringLiteral::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
1033 1034 1035 1036 1037 1038 1039 1040 1041
{
    typedef CppQuickFixOperation::Ptr OperationPtr;

    Type type = TypeNone;
    QByteArray enclosingFunction;
    const QList<AST *> &path = interface->path();
    CppRefactoringFilePtr file = interface->currentFile();
    ExpressionAST *literal = WrapStringLiteral::analyze(path, file, &type, &enclosingFunction);
    if (!literal || type == TypeNone)
1042
        return;
1043 1044 1045
    if ((type == TypeChar && enclosingFunction == "QLatin1Char")
        || isQtStringLiteral(enclosingFunction)
        || isQtStringTranslation(enclosingFunction))
1046
        return;
1047

1048 1049 1050
    const int priority = path.size() - 1; // very high priority
    if (type == TypeChar) {
        unsigned actions = EncloseInQLatin1CharAction;
1051
        QString description = msgQtStringLiteralDescription(WrapStringLiteral::replacement(actions));
1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062
        result << OperationPtr(new Operation(interface, priority, actions,
                                             description, literal));
        if (NumericLiteralAST *charLiteral = literal->asNumericLiteral()) {
            const QByteArray contents(file->tokenAt(charLiteral->literal_token).identifier->chars());
            if (!charToStringEscapeSequences(contents).isEmpty()) {
                actions = DoubleQuoteAction | ConvertEscapeSequencesToStringAction;
                description = QApplication::translate("CppTools::QuickFix",
                              "Convert to String Literal");
                result << OperationPtr(new Operation(interface, priority, actions,
                                                     description, literal));
            }
1063
        }
1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091
    } else {
        const unsigned objectiveCActions = type == TypeObjCString ?
                                           unsigned(RemoveObjectiveCAction) : 0u;
        unsigned actions = 0;
        if (StringLiteralAST *stringLiteral = literal->asStringLiteral()) {
            const QByteArray