cppquickfixes.cpp 76.7 KB
Newer Older
1
2
3
4
/**************************************************************************
**
** This file is part of Qt Creator
**
hjk's avatar
hjk committed
5
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
6
**
Eike Ziller's avatar
Eike Ziller committed
7
** Contact: http://www.qt-project.org/
8
9
10
11
**
**
** GNU Lesser General Public License Usage
**
hjk's avatar
hjk committed
12
13
14
15
16
17
** 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.
18
**
con's avatar
con committed
19
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
con's avatar
con committed
21
22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23
24
25
26
27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
28
29
30
**
**************************************************************************/

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

#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>
57
#include <cpptools/ModelManagerInterface.h>
58
59
#include <cplusplus/CppRewriter.h>
#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
81
82
83
using namespace TextEditor;
using namespace CPlusPlus;
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:
Leandro Melo's avatar
Leandro Melo committed
107
    virtual QList<CppQuickFixOperation::Ptr> match(const QSharedPointer<const CppQuickFixAssistInterface> &interface)
108
109
    {
        QList<CppQuickFixOperation::Ptr> result;
110
        CppRefactoringFilePtr file = interface->currentFile();
111

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

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

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

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

        QString replacement;

    public:
Leandro Melo's avatar
Leandro Melo committed
158
159
160
        Operation(const QSharedPointer<const CppQuickFixAssistInterface> &interface,
            int priority, BinaryExpressionAST *binary, Kind invertToken)
            : CppQuickFixOperation(interface, priority)
161
            , binary(binary), nested(0), negation(0)
162
163
164
165
166
167
168
        {
            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
169
                nested = interface->path()[priority - 1]->asNestedExpression();
170
171
172

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

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

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

/*
    Rewrite
    a op b

    As
    b flipop a
209
210

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

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

        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
249
250
251
252
253
254
255
256
257
258
        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 result;
        }

        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
266
        return result;
    }

private:
    class Operation: public CppQuickFixOperation
    {
    public:
Leandro Melo's avatar
Leandro Melo committed
267
268
269
        Operation(const QSharedPointer<const CppQuickFixAssistInterface> &interface,
                  int priority, BinaryExpressionAST *binary, QString replacement)
            : CppQuickFixOperation(interface)
270
271
272
273
274
275
276
277
278
279
280
281
282
283
            , 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);
        }

284
        virtual void performChanges(const CppRefactoringFilePtr &currentFile, const CppRefactoringChanges &)
285
286
287
        {
            ChangeSet changes;

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

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

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

/*
    Rewrite
    !a && !b

    As
    !(a || b)
308
309

    Activates on: &&
310
311
312
313
*/
class RewriteLogicalAndOp: public CppQuickFixFactory
{
public:
Leandro Melo's avatar
Leandro Melo committed
314
    virtual QList<QuickFixOperation::Ptr> match(const QSharedPointer<const CppQuickFixAssistInterface> &interface)
315
316
317
    {
        QList<QuickFixOperation::Ptr> result;
        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
329
330

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

        if (! expression)
            return result;

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

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
355
356
            op->setDescription(QApplication::translate("CppTools::QuickFix", "Rewrite Condition Using ||"));
            op->setPriority(index);
            result.append(op);
        }

        return result;
    }

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

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

366
        virtual void performChanges(const CppRefactoringFilePtr &currentFile, const CppRefactoringChanges &)
367
368
        {
            ChangeSet changes;
369
370
371
372
373
            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);
374
375
376
            changes.insert(start, QLatin1String("!("));
            changes.insert(end, QLatin1String(")"));

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

private:
    ASTMatcher matcher;
};

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

    As
    int *a;
    int b;
394
395

    Activates on: the type or the variable names.
396
*/
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
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:
Leandro Melo's avatar
Leandro Melo committed
427
    virtual QList<CppQuickFixOperation::Ptr> match(const QSharedPointer<const CppQuickFixAssistInterface> &interface)
428
429
430
    {
        QList<CppQuickFixOperation::Ptr> result;
        CoreDeclaratorAST *core_declarator = 0;
Leandro Melo's avatar
Leandro Melo committed
431
        const QList<AST *> &path = interface->path();
432
        CppRefactoringFilePtr file = interface->currentFile();
433
434
435
436
437
438
439
440
441
442
443

        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;

444
                    const int cursorPosition = file->cursor().selectionStart();
445

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

                    if (cursorPosition >= startOfDeclSpecifier && cursorPosition <= endOfDeclSpecifier) {
                        // the AST node under cursor is a specifier.
Leandro Melo's avatar
Leandro Melo committed
451
                        return singleResult(new Operation(interface, index, declaration));
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.
Leandro Melo's avatar
Leandro Melo committed
456
                        return singleResult(new Operation(interface, index, declaration));
457
458
459
460
461
462
463
464
465
466
467
468
469
470
                    }
                }

                break;
            }
        }

        return result;
    }

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

479
        virtual void performChanges(const CppRefactoringFilePtr &currentFile, const CppRefactoringChanges &)
480
481
482
483
        {
            ChangeSet changes;

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

            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(" "));
496
                changes.move(currentFile->range(declarator), insertPos);
497
498
                changes.insert(insertPos, QLatin1String(";"));

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

                prevDeclarator = declarator;
            }

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

    private:
        SimpleDeclarationAST *declaration;
    };
};

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

    if (a)
        b;
    becomes
    if (a) {
        b;
    }
525
526

    Activates on: the if
527
528
529
530
*/
class AddBracesToIfOp: public CppQuickFixFactory
{
public:
Leandro Melo's avatar
Leandro Melo committed
531
    virtual QList<CppQuickFixOperation::Ptr> match(const QSharedPointer<const CppQuickFixAssistInterface> &interface)
532
    {
Leandro Melo's avatar
Leandro Melo committed
533
        const QList<AST *> &path = interface->path();
534
535
536
537

        // 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
538
        if (ifStatement && interface->isCursorOn(ifStatement->if_token) && ifStatement->statement
539
            && ! ifStatement->statement->asCompoundStatement()) {
Leandro Melo's avatar
Leandro Melo committed
540
            return singleResult(new Operation(interface, index, ifStatement->statement));
541
542
543
544
545
546
547
        }

        // 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
548
                && interface->isCursorOn(ifStatement->statement)
549
                && ! ifStatement->statement->asCompoundStatement()) {
Leandro Melo's avatar
Leandro Melo committed
550
                return singleResult(new Operation(interface, index, ifStatement->statement));
551
552
553
554
555
556
557
558
559
560
561
562
563
            }
        }

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

        return noResult();
    }

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

572
        virtual void performChanges(const CppRefactoringFilePtr &currentFile, const CppRefactoringChanges &)
573
574
575
        {
            ChangeSet changes;

576
            const int start = currentFile->endOf(_statement->firstToken() - 1);
577
578
            changes.insert(start, QLatin1String(" {"));

579
            const int end = currentFile->endOf(_statement->lastToken() - 1);
580
            changes.insert(end, QLatin1String("\n}"));
581

582
583
584
            currentFile->setChangeSet(changes);
            currentFile->appendIndentRange(Utils::ChangeSet::Range(start, end));
            currentFile->apply();
585
586
587
588
589
590
591
592
593
594
595
596
597
598
        }

    private:
        StatementAST *_statement;
    };
};

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

    With
    Type name = foo;
    if (name) {...}
599
600

    Activates on: the name of the introduced variable
601
602
603
604
*/
class MoveDeclarationOutOfIfOp: public CppQuickFixFactory
{
public:
Leandro Melo's avatar
Leandro Melo committed
605
    virtual QList<CppQuickFixOperation::Ptr> match(const QSharedPointer<const CppQuickFixAssistInterface> &interface)
606
    {
Leandro Melo's avatar
Leandro Melo committed
607
608
        const QList<AST *> &path = interface->path();
        QSharedPointer<Operation> op(new Operation(interface));
609
610
611
612
613
614
615
616
617
618

        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)
                        return noResult();

Leandro Melo's avatar
Leandro Melo committed
619
                    if (interface->isCursorOn(op->core)) {
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
                        QList<CppQuickFixOperation::Ptr> result;
                        op->setPriority(index);
                        result.append(op);
                        return result;
                    }
                }
            }
        }

        return noResult();
    }

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

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

646
        virtual void performChanges(const CppRefactoringFilePtr &currentFile, const CppRefactoringChanges &)
647
648
649
        {
            ChangeSet changes;

650
            changes.copy(currentFile->range(core), currentFile->startOf(condition));
651

652
653
            int insertPos = currentFile->startOf(pattern);
            changes.move(currentFile->range(condition), insertPos);
654
655
            changes.insert(insertPos, QLatin1String(";\n"));

656
657
658
            currentFile->setChangeSet(changes);
            currentFile->appendIndentRange(currentFile->range(pattern));
            currentFile->apply();
659
660
661
662
        }

        ASTMatcher matcher;
        ASTPatternBuilder mk;
663
        CPPEditorWidget *editor;
664
665
666
667
668
669
670
671
672
673
674
675
676
        ConditionAST *condition;
        IfStatementAST *pattern;
        CoreDeclaratorAST *core;
    };
};

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

    With
    Type name;
    while ((name = foo()) != 0) {...}
677
678

    Activates on: the name of the introduced variable
679
680
681
682
*/
class MoveDeclarationOutOfWhileOp: public CppQuickFixFactory
{
public:
Leandro Melo's avatar
Leandro Melo committed
683
    virtual QList<CppQuickFixOperation::Ptr> match(const QSharedPointer<const CppQuickFixAssistInterface> &interface)
684
    {
Leandro Melo's avatar
Leandro Melo committed
685
686
        const QList<AST *> &path = interface->path();
        QSharedPointer<Operation> op(new Operation(interface));
687
688
689
690
691
692
693
694
695
696
697

        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)
                        return noResult();

698
                    else if (! declarator->equal_token)
699
700
701
702
703
                        return noResult();

                    else if (! declarator->initializer)
                        return noResult();

Leandro Melo's avatar
Leandro Melo committed
704
                    if (interface->isCursorOn(op->core)) {
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
                        QList<CppQuickFixOperation::Ptr> result;
                        op->setPriority(index);
                        result.append(op);
                        return result;
                    }
                }
            }
        }

        return noResult();
    }

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

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

731
        virtual void performChanges(const CppRefactoringFilePtr &currentFile, const CppRefactoringChanges &)
732
733
734
        {
            ChangeSet changes;

735
736
            changes.insert(currentFile->startOf(condition), QLatin1String("("));
            changes.insert(currentFile->endOf(condition), QLatin1String(") != 0"));
737

738
739
740
741
            int insertPos = currentFile->startOf(pattern);
            const int conditionStart = currentFile->startOf(condition);
            changes.move(conditionStart, currentFile->startOf(core), insertPos);
            changes.copy(currentFile->range(core), insertPos);
742
743
            changes.insert(insertPos, QLatin1String(";\n"));

744
745
746
            currentFile->setChangeSet(changes);
            currentFile->appendIndentRange(currentFile->range(pattern));
            currentFile->apply();
747
748
749
750
        }

        ASTMatcher matcher;
        ASTPatternBuilder mk;
751
        CPPEditorWidget *editor;
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
        ConditionAST *condition;
        WhileStatementAST *pattern;
        CoreDeclaratorAST *core;
    };
};

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

  with
     if (something) {
        if (something_else) {
        }
     }

  and
    if (something || something_else)
      x;

  with
    if (something)
      x;
    else if (something_else)
      x;
778
779

    Activates on: && or ||
780
781
782
783
*/
class SplitIfStatementOp: public CppQuickFixFactory
{
public:
Leandro Melo's avatar
Leandro Melo committed
784
    virtual QList<CppQuickFixOperation::Ptr> match(const QSharedPointer<const CppQuickFixAssistInterface> &interface)
785
786
    {
        IfStatementAST *pattern = 0;
Leandro Melo's avatar
Leandro Melo committed
787
        const QList<AST *> &path = interface->path();
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807

        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)
            return noResult();

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

808
            Token binaryToken = interface->currentFile()->tokenAt(condition->binary_op_token);
809
810
811
812
813
814
815
816
817
818
819
820
821

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

Leandro Melo's avatar
Leandro Melo committed
822
823
            if (interface->isCursorOn(condition->binary_op_token))
                return singleResult(new Operation(interface, index, pattern, condition));
824
825
826
827
828
829
830
831
832
        }

        return noResult();
    }

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

843
        virtual void performChanges(const CppRefactoringFilePtr &currentFile, const CppRefactoringChanges &)
844
        {
845
            const Token binaryToken = currentFile->tokenAt(condition->binary_op_token);
846
847

            if (binaryToken.is(T_AMPER_AMPER))
848
                splitAndCondition(currentFile);
849
            else
850
                splitOrCondition(currentFile);
851
852
        }

853
        void splitAndCondition(CppRefactoringFilePtr currentFile)
854
855
856
        {
            ChangeSet changes;

857
            int startPos = currentFile->startOf(pattern);
858
            changes.insert(startPos, QLatin1String("if ("));
859
            changes.move(currentFile->range(condition->left_expression), startPos);
860
861
            changes.insert(startPos, QLatin1String(") {\n"));

862
863
864
            const int lExprEnd = currentFile->endOf(condition->left_expression);
            changes.remove(lExprEnd, currentFile->startOf(condition->right_expression));
            changes.insert(currentFile->endOf(pattern), QLatin1String("\n}"));
865

866
867
868
            currentFile->setChangeSet(changes);
            currentFile->appendIndentRange(currentFile->range(pattern));
            currentFile->apply();
869
870
        }

871
        void splitOrCondition(CppRefactoringFilePtr currentFile)
872
873
874
875
876
877
        {
            ChangeSet changes;

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

878
            int insertPos = currentFile->endOf(ifTrueStatement);
879
880
881
882
883
884
            if (compoundStatement)
                changes.insert(insertPos, QLatin1String(" "));
            else
                changes.insert(insertPos, QLatin1String("\n"));
            changes.insert(insertPos, QLatin1String("else if ("));

885
886
            const int rExprStart = currentFile->startOf(condition->right_expression);
            changes.move(rExprStart, currentFile->startOf(pattern->rparen_token), insertPos);
887
888
            changes.insert(insertPos, QLatin1String(")"));

889
890
            const int rParenEnd = currentFile->endOf(pattern->rparen_token);
            changes.copy(rParenEnd, currentFile->endOf(pattern->statement), insertPos);
891

892
893
            const int lExprEnd = currentFile->endOf(condition->left_expression);
            changes.remove(lExprEnd, currentFile->startOf(condition->right_expression));
894

895
896
897
            currentFile->setChangeSet(changes);
            currentFile->appendIndentRange(currentFile->range(pattern));
            currentFile->apply();
898
899
900
901
902
903
904
905
906
907
        }

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

/*
  Replace
908
909
910
911
912
913
    "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')
914
915
916
  Except if they are already enclosed in
    QLatin1Char, QT_TRANSLATE_NOOP, tr,
    trUtf8, QLatin1Literal, QLatin1String
917

918
    Activates on: the string or character literal
919
*/
920
921
922
923
924
925
926

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

927
928
929
930
931
static inline QString msgQtStringLiteralDescription(const QString &replacement)
{
    return QApplication::translate("CppTools::QuickFix", "Enclose in %1(...)").arg(replacement);
}

932
933
934
class WrapStringLiteral: public CppQuickFixFactory
{
public:
935
936
937
938
939
940
941
942
943
944
945
946
947
    typedef const QSharedPointer<const CppQuickFixAssistInterface> AssistInterfacePtr;

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

948
949
    enum Type { TypeString, TypeObjCString, TypeChar, TypeNone };

950
951
952
953
954
955
956
957
958
959
960
961
    virtual QList<CppQuickFixOperation::Ptr> match(const AssistInterfacePtr &interface);
    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
962
    {
963
964
965
966
    public:
        Operation(const AssistInterfacePtr &interface, int priority,
                  unsigned actions, const QString &description, ExpressionAST *literal,
                  const QString &translationContext = QString());
967

968
969
970
971
972
973
974
        virtual void performChanges(const CppRefactoringFilePtr &currentFile, const CppRefactoringChanges &);
    private:
        const unsigned m_actions;
        ExpressionAST *m_literal;
        const QString m_translationContext;
    };
};
975

976
977
978
/* 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...) */
979

980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
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;
For faster browsing, not all history is shown. View entire blame