cppquickfix.cpp 34.8 KB
Newer Older
1
2
3
4
/**************************************************************************
**
** This file is part of Qt Creator
**
hjk's avatar
hjk committed
5
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** 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.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/

#include "cppquickfix.h"
#include "cppeditor.h"
32

33
#include <cplusplus/ASTPath.h>
34
#include <cplusplus/CppDocument.h>
35
#include <cplusplus/ResolveExpression.h>
36

37
#include <TranslationUnit.h>
38
39
#include <ASTVisitor.h>
#include <AST.h>
40
41
#include <ASTPatternBuilder.h>
#include <ASTMatcher.h>
42
#include <Token.h>
43
44
45
46
47
48
#include <Type.h>
#include <CoreTypes.h>
#include <Symbol.h>
#include <Symbols.h>
#include <Name.h>
#include <Literals.h>
49

Roberto Raggi's avatar
Roberto Raggi committed
50
#include <cppeditor/cpprefactoringchanges.h>
51
#include <cpptools/cpptoolsconstants.h>
52
#include <cpptools/cppmodelmanagerinterface.h>
53

Robert Loehning's avatar
Robert Loehning committed
54
#include <QtGui/QApplication>
55
#include <QtGui/QTextBlock>
56
57
58

using namespace CppEditor::Internal;
using namespace CPlusPlus;
59
using namespace Utils;
60

61
62
namespace {

63
64
65
66
class CppQuickFixState: public TextEditor::QuickFixState
{
public:
    QList<CPlusPlus::AST *> path;
67
    SemanticInfo info;
68
69
};

70
71
/*
    Rewrite
72
73
74
    a op b -> !(a invop b)
    (a op b) -> !(a invop b)
    !(a op b) -> (a invob b)
75
*/
76
class UseInverseOp: public CppQuickFixOperation
77
78
{
public:
79
80
    UseInverseOp(TextEditor::BaseTextEditor *editor)
        : CppQuickFixOperation(editor), binary(0), nested(0), negation(0)
81
82
83
84
    {}

    virtual QString description() const
    {
Robert Loehning's avatar
Robert Loehning committed
85
        return QApplication::translate("CppTools::QuickFix", "Rewrite Using %1").arg(replacement);
86
87
88
89
90
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
    }

    virtual int match(const QList<AST *> &path)
    {
        int index = path.size() - 1;
        binary = path.at(index)->asBinaryExpression();
        if (! binary)
            return -1;
        if (! isCursorOn(binary->binary_op_token))
            return -1;

        CPlusPlus::Kind invertToken;
        switch (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 -1;
        }

        CPlusPlus::Token tok;
        tok.f.kind = invertToken;
        replacement = QLatin1String(tok.spell());
124
125
126
127
128
129
130
131
132
133
134
135

        // check for enclosing nested expression
        if (index - 1 >= 0)
            nested = path[index - 1]->asNestedExpression();

        // check for ! before parentheses
        if (nested && index - 2 >= 0) {
            negation = path[index - 2]->asUnaryExpression();
            if (negation && ! tokenAt(negation->unary_op_token).is(T_EXCLAIM))
                negation = 0;
        }

136
137
138
        return index;
    }

139
    virtual void createChanges()
140
    {
141
        ChangeSet changes;
142
143
        if (negation) {
            // can't remove parentheses since that might break precedence
144
            remove(&changes, negation->unary_op_token);
145
        } else if (nested) {
146
            changes.insert(startOf(nested), "!");
147
        } else {
148
149
            changes.insert(startOf(binary), "!(");
            changes.insert(endOf(binary), ")");
150
        }
151
        replace(&changes, binary->binary_op_token, replacement);
152
        refactoringChanges()->changeFile(fileName(), changes);
153
154
155
156
    }

private:
    BinaryExpressionAST *binary;
157
158
159
    NestedExpressionAST *nested;
    UnaryExpressionAST *negation;

160
161
162
163
164
165
166
167
168
169
    QString replacement;
};

/*
    Rewrite
    a op b

    As
    b flipop a
*/
170
class FlipBinaryOp: public CppQuickFixOperation
171
172
{
public:
173
174
    FlipBinaryOp(TextEditor::BaseTextEditor *editor)
        : CppQuickFixOperation(editor), binary(0)
175
176
177
178
179
180
    {}


    virtual QString description() const
    {
        if (replacement.isEmpty())
Robert Loehning's avatar
Robert Loehning committed
181
            return QApplication::translate("CppTools::QuickFix", "Swap Operands");
182
        else
Robert Loehning's avatar
Robert Loehning committed
183
            return QApplication::translate("CppTools::QuickFix", "Rewrite Using %1").arg(replacement);
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
    }

    virtual int match(const QList<AST *> &path)
    {
        int index = path.size() - 1;
        binary = path.at(index)->asBinaryExpression();
        if (! binary)
            return -1;
        if (! isCursorOn(binary->binary_op_token))
            return -1;

        CPlusPlus::Kind flipToken;
        switch (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 -1;
        }

        if (flipToken != T_EOF_SYMBOL) {
            CPlusPlus::Token tok;
            tok.f.kind = flipToken;
            replacement = QLatin1String(tok.spell());
        }
        return index;
    }

227
    virtual void createChanges()
228
    {
229
230
231
        ChangeSet changes;

        flip(&changes, binary->left_expression, binary->right_expression);
232
        if (! replacement.isEmpty())
233
234
            replace(&changes, binary->binary_op_token, replacement);

235
        refactoringChanges()->changeFile(fileName(), changes);
236
237
238
239
240
241
242
243
244
245
246
247
248
249
    }

private:
    BinaryExpressionAST *binary;
    QString replacement;
};

/*
    Rewrite
    !a && !b

    As
    !(a || b)
*/
250
class RewriteLogicalAndOp: public CppQuickFixOperation
251
252
{
public:
253
254
    RewriteLogicalAndOp(TextEditor::BaseTextEditor *editor)
        : CppQuickFixOperation(editor), left(0), right(0), pattern(0)
255
256
257
258
    {}

    virtual QString description() const
    {
Robert Loehning's avatar
Robert Loehning committed
259
        return QApplication::translate("CppTools::QuickFix", "Rewrite Condition Using ||");
260
261
    }

262
    virtual int match(const QList<AST *> &path)
263
    {
264
265
266
267
268
269
270
271
272
273
274
275
        BinaryExpressionAST *expression = 0;

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

        if (! expression)
            return -1;

276
        if (! isCursorOn(expression->binary_op_token))
277
278
            return -1;

279
280
281
282
283
284
285
286
        left = mk.UnaryExpression();
        right = mk.UnaryExpression();
        pattern = mk.BinaryExpression(left, right);

        if (expression->match(pattern, &matcher) &&
                tokenAt(pattern->binary_op_token).is(T_AMPER_AMPER) &&
                tokenAt(left->unary_op_token).is(T_EXCLAIM) &&
                tokenAt(right->unary_op_token).is(T_EXCLAIM)) {
287
            return index;
288
289
        }

290
        return -1;
291
292
    }

293
    virtual void createChanges()
294
    {
295
296
297
298
299
300
301
302
303
        ChangeSet changes;
        replace(&changes, pattern->binary_op_token, QLatin1String("||"));
        remove(&changes, left->unary_op_token);
        remove(&changes, right->unary_op_token);
        const int start = startOf(pattern);
        const int end = endOf(pattern);
        changes.insert(start, QLatin1String("!("));
        changes.insert(end, QLatin1String(")"));

304
305
        refactoringChanges()->changeFile(fileName(), changes);
        refactoringChanges()->reindent(fileName(), range(start, end));
306
307
308
309
310
311
312
313
314
315
    }

private:
    ASTMatcher matcher;
    ASTPatternBuilder mk;
    UnaryExpressionAST *left;
    UnaryExpressionAST *right;
    BinaryExpressionAST *pattern;
};

316
class SplitSimpleDeclarationOp: public CppQuickFixOperation
317
318
{
public:
319
320
    SplitSimpleDeclarationOp(TextEditor::BaseTextEditor *editor)
        : CppQuickFixOperation(editor), declaration(0)
321
322
323
324
    {}

    virtual QString description() const
    {
Robert Loehning's avatar
Robert Loehning committed
325
        return QApplication::translate("CppTools::QuickFix", "Split Declaration");
326
327
328
329
    }

    bool checkDeclaration(SimpleDeclarationAST *declaration) const
    {
330
331
332
        if (! declaration->semicolon_token)
            return false;

333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
        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;
    }

355
    virtual int match(const QList<AST *> &path)
356
357
358
359
360
361
362
363
364
365
366
367
368
369
    {
        CoreDeclaratorAST *core_declarator = 0;

        int index = path.size() - 1;
        for (; 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)) {
                    declaration = simpleDecl;

370
                    const int cursorPosition = selectionStart();
371
372
373
374
375
376
377

                    const int startOfDeclSpecifier = startOf(declaration->decl_specifier_list->firstToken());
                    const int endOfDeclSpecifier = endOf(declaration->decl_specifier_list->lastToken() - 1);

                    if (cursorPosition >= startOfDeclSpecifier && cursorPosition <= endOfDeclSpecifier)
                        return index; // the AST node under cursor is a specifier.

378
                    if (core_declarator && isCursorOn(core_declarator))
379
                        return index; // got a core-declarator under the text cursor.
380
381
382
383
384
385
386
387
388
                }

                break;
            }
        }

        return -1;
    }

389
    virtual void createChanges()
390
    {
391
392
        ChangeSet changes;

393
        SpecifierListAST *specifiers = declaration->decl_specifier_list;
394
395
396
        int declSpecifiersStart = startOf(specifiers->firstToken());
        int declSpecifiersEnd = endOf(specifiers->lastToken() - 1);
        int insertPos = endOf(declaration->semicolon_token);
397

398
        DeclaratorAST *prevDeclarator = declaration->declarator_list->value;
399
400
401
402

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

403
404
405
406
407
            changes.insert(insertPos, QLatin1String("\n"));
            changes.copy(declSpecifiersStart, declSpecifiersEnd - declSpecifiersStart, insertPos);
            changes.insert(insertPos, QLatin1String(" "));
            move(&changes, declarator, insertPos);
            changes.insert(insertPos, QLatin1String(";"));
408

409
410
            const int prevDeclEnd = endOf(prevDeclarator);
            changes.remove(prevDeclEnd, startOf(declarator) - prevDeclEnd);
411

412
413
            prevDeclarator = declarator;
        }
414

415
416
        refactoringChanges()->changeFile(fileName(), changes);
        refactoringChanges()->reindent(fileName(), range(startOf(declaration->firstToken()),
417
                                                            endOf(declaration->lastToken() - 1)));
418
419
420
421
422
423
    }

private:
    SimpleDeclarationAST *declaration;
};

424
425
426
427
/*
    Add curly braces to a if statement that doesn't already contain a
    compound statement.
*/
428
class AddBracesToIfOp: public CppQuickFixOperation
429
430
{
public:
431
432
    AddBracesToIfOp(TextEditor::BaseTextEditor *editor)
        : CppQuickFixOperation(editor), _statement(0)
433
434
435
436
    {}

    virtual QString description() const
    {
Robert Loehning's avatar
Robert Loehning committed
437
        return QApplication::translate("CppTools::QuickFix", "Add Curly Braces");
438
439
    }

440
    virtual int match(const QList<AST *> &path)
441
442
443
444
    {
        // show when we're on the 'if' of an if statement
        int index = path.size() - 1;
        IfStatementAST *ifStatement = path.at(index)->asIfStatement();
445
        if (ifStatement && isCursorOn(ifStatement->if_token)
446
447
448
449
450
451
452
453
454
            && ! ifStatement->statement->asCompoundStatement()) {
            _statement = ifStatement->statement;
            return index;
        }

        // 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();
455
456
            if (ifStatement && ifStatement->statement
                && isCursorOn(ifStatement->statement)
457
458
459
460
461
462
463
464
465
466
467
468
                && ! ifStatement->statement->asCompoundStatement()) {
                _statement = ifStatement->statement;
                return index;
            }
        }

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

        return -1;
    }

469
    virtual void createChanges()
470
    {
471
472
473
474
475
476
477
478
        ChangeSet changes;

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

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

479
480
        refactoringChanges()->changeFile(fileName(), changes);
        refactoringChanges()->reindent(fileName(), range(start, end));
481
482
483
484
485
486
487
488
489
490
491
492
493
494
    }

private:
    StatementAST *_statement;
};

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

    With
    Type name = foo;
    if (name) {...}
*/
495
class MoveDeclarationOutOfIfOp: public CppQuickFixOperation
496
497
{
public:
498
499
    MoveDeclarationOutOfIfOp(TextEditor::BaseTextEditor *editor)
        : CppQuickFixOperation(editor), condition(0), pattern(0), core(0)
500
501
502
503
    {}

    virtual QString description() const
    {
Robert Loehning's avatar
Robert Loehning committed
504
        return QApplication::translate("CppTools::QuickFix", "Move Declaration out of Condition");
505
506
    }

507
    virtual int match(const QList<AST *> &path)
508
509
510
511
512
513
514
515
516
517
518
519
520
    {
        condition = mk.Condition();
        pattern = mk.IfStatement(condition);

        int index = path.size() - 1;
        for (; index != -1; --index) {
            if (IfStatementAST *statement = path.at(index)->asIfStatement()) {
                if (statement->match(pattern, &matcher) && condition->declarator) {
                    DeclaratorAST *declarator = condition->declarator;
                    core = declarator->core_declarator;
                    if (! core)
                        return -1;

521
                    if (isCursorOn(core))
522
523
524
525
526
527
528
529
                        return index;
                }
            }
        }

        return -1;
    }

530
    virtual void createChanges()
531
    {
532
        ChangeSet changes;
533

534
        copy(&changes, core, startOf(condition));
535
536

        int insertPos = startOf(pattern);
537
538
539
        move(&changes, condition, insertPos);
        changes.insert(insertPos, QLatin1String(";\n"));

540
541
        refactoringChanges()->changeFile(fileName(), changes);
        refactoringChanges()->reindent(fileName(), range(startOf(pattern),
542
                                                            endOf(pattern)));
543
544
545
546
547
548
549
550
551
552
553
    }

private:
    ASTMatcher matcher;
    ASTPatternBuilder mk;
    CPPEditor *editor;
    ConditionAST *condition;
    IfStatementAST *pattern;
    CoreDeclaratorAST *core;
};

554
555
556
557
558
559
560
561
/*
    Replace
    while (Type name = foo()) {...}

    With
    Type name;
    while ((name = foo()) != 0) {...}
*/
562
class MoveDeclarationOutOfWhileOp: public CppQuickFixOperation
563
564
{
public:
565
566
    MoveDeclarationOutOfWhileOp(TextEditor::BaseTextEditor *editor)
        : CppQuickFixOperation(editor), condition(0), pattern(0), core(0)
567
568
569
570
    {}

    virtual QString description() const
    {
Robert Loehning's avatar
Robert Loehning committed
571
        return QApplication::translate("CppTools::QuickFix", "Move Declaration out of Condition");
572
573
    }

574
    virtual int match(const QList<AST *> &path)
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
    {
        condition = mk.Condition();
        pattern = mk.WhileStatement(condition);

        int index = path.size() - 1;
        for (; index != -1; --index) {
            if (WhileStatementAST *statement = path.at(index)->asWhileStatement()) {
                if (statement->match(pattern, &matcher) && condition->declarator) {
                    DeclaratorAST *declarator = condition->declarator;
                    core = declarator->core_declarator;

                    if (! core)
                        return -1;

                    else if (! declarator->equals_token)
                        return -1;

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

595
                    if (isCursorOn(core))
596
597
598
599
600
601
602
603
                        return index;
                }
            }
        }

        return -1;
    }

604
    virtual void createChanges()
605
    {
606
        ChangeSet changes;
607

608
609
        changes.insert(startOf(condition), QLatin1String("("));
        changes.insert(endOf(condition), QLatin1String(") != 0"));
610
611

        int insertPos = startOf(pattern);
612
613
614
615
616
        const int conditionStart = startOf(condition);
        changes.move(conditionStart, startOf(core) - conditionStart, insertPos);
        copy(&changes, core, insertPos);
        changes.insert(insertPos, QLatin1String(";\n"));

617
618
        refactoringChanges()->changeFile(fileName(), changes);
        refactoringChanges()->reindent(fileName(), range(startOf(pattern),
619
                                                            endOf(pattern)));
620
621
622
623
624
625
626
627
628
629
630
    }

private:
    ASTMatcher matcher;
    ASTPatternBuilder mk;
    CPPEditor *editor;
    ConditionAST *condition;
    WhileStatementAST *pattern;
    CoreDeclaratorAST *core;
};

Roberto Raggi's avatar
Roberto Raggi committed
631
632
633
634
635
636
637
638
639
640
/*
  Replace
     if (something && something_else) {
     }

  with
     if (something) {
        if (something_else) {
        }
     }
Roberto Raggi's avatar
Roberto Raggi committed
641
642
643
644
645
646
647
648
649
650

  and
    if (something || something_else)
      x;

  with
    if (something)
      x;
    else if (something_else)
      x;
Roberto Raggi's avatar
Roberto Raggi committed
651
*/
652
class SplitIfStatementOp: public CppQuickFixOperation
Roberto Raggi's avatar
Roberto Raggi committed
653
654
{
public:
655
656
    SplitIfStatementOp(TextEditor::BaseTextEditor *editor)
        : CppQuickFixOperation(editor), condition(0), pattern(0)
Roberto Raggi's avatar
Roberto Raggi committed
657
658
659
660
    {}

    virtual QString description() const
    {
Robert Loehning's avatar
Robert Loehning committed
661
        return QApplication::translate("CppTools::QuickFix", "Split if Statement");
Roberto Raggi's avatar
Roberto Raggi committed
662
663
    }

664
    virtual int match(const QList<AST *> &path)
Roberto Raggi's avatar
Roberto Raggi committed
665
    {
666
        pattern = 0;
Roberto Raggi's avatar
Roberto Raggi committed
667

668
669
670
671
672
673
674
675
676
        int index = path.size() - 1;
        for (; index != -1; --index) {
            AST *node = path.at(index);
            if (IfStatementAST *stmt = node->asIfStatement()) {
                pattern = stmt;
                break;
            }
        }

677
        if (! pattern || ! pattern->statement)
678
679
            return -1;

680
        unsigned splitKind = 0;
681
682
683
684
685
686
687
        for (++index; index < path.size(); ++index) {
            AST *node = path.at(index);
            condition = node->asBinaryExpression();
            if (! condition)
                return -1;

            Token binaryToken = tokenAt(condition->binary_op_token);
688
689
690
691
692
693
694
695
696
697

            // 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 -1;
                // we can't reliably split &&s in ifs with an else branch
                if (splitKind == T_AMPER_AMPER && pattern->else_statement)
                    return -1;
            } else if (splitKind != binaryToken.kind()) {
698
699
                return -1;
            }
700
701
702

            if (isCursorOn(condition->binary_op_token))
                return index;
703
704
705
        }

        return -1;
Roberto Raggi's avatar
Roberto Raggi committed
706
707
    }

708
    virtual void createChanges()
Roberto Raggi's avatar
Roberto Raggi committed
709
    {
710
        Token binaryToken = tokenAt(condition->binary_op_token);
Roberto Raggi's avatar
Roberto Raggi committed
711

712
        if (binaryToken.is(T_AMPER_AMPER))
Roberto Raggi's avatar
Roberto Raggi committed
713
714
715
716
717
718
719
            splitAndCondition();
        else
            splitOrCondition();
    }

    void splitAndCondition()
    {
720
        ChangeSet changes;
Roberto Raggi's avatar
Roberto Raggi committed
721

722
        int startPos = startOf(pattern);
723
724
725
726
727
728
729
730
731
        changes.insert(startPos, QLatin1String("if ("));
        move(&changes, condition->left_expression, startPos);
        changes.insert(startPos, QLatin1String(") {\n"));

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

732
733
        refactoringChanges()->changeFile(fileName(), changes);
        refactoringChanges()->reindent(fileName(), range(startOf(pattern),
734
                                                            endOf(pattern)));
Roberto Raggi's avatar
Roberto Raggi committed
735
736
    }

Roberto Raggi's avatar
Roberto Raggi committed
737
738
    void splitOrCondition()
    {
739
740
        ChangeSet changes;

Roberto Raggi's avatar
Roberto Raggi committed
741
742
743
        StatementAST *ifTrueStatement = pattern->statement;
        CompoundStatementAST *compoundStatement = ifTrueStatement->asCompoundStatement();

744
        int insertPos = endOf(ifTrueStatement);
Roberto Raggi's avatar
Roberto Raggi committed
745
        if (compoundStatement)
746
            changes.insert(insertPos, QLatin1String(" "));
Roberto Raggi's avatar
Roberto Raggi committed
747
        else
748
749
750
751
752
753
754
            changes.insert(insertPos, QLatin1String("\n"));
        changes.insert(insertPos, QLatin1String("else if ("));

        const int rExprStart = startOf(condition->right_expression);
        changes.move(rExprStart, startOf(pattern->rparen_token) - rExprStart,
                     insertPos);
        changes.insert(insertPos, QLatin1String(")"));
755

756
757
758
759
760
761
        const int rParenEnd = endOf(pattern->rparen_token);
        changes.copy(rParenEnd, endOf(pattern->statement) - rParenEnd, insertPos);

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

762
763
        refactoringChanges()->changeFile(fileName(), changes);
        refactoringChanges()->reindent(fileName(), range(startOf(pattern),
764
                                                            endOf(pattern)));
Roberto Raggi's avatar
Roberto Raggi committed
765
766
    }

Roberto Raggi's avatar
Roberto Raggi committed
767
768
769
770
private:
    BinaryExpressionAST *condition;
    IfStatementAST *pattern;
};
771

772
773
774
775
776
777
/*
  Replace
    "abcd"
  With
    QLatin1String("abcd")
*/
778
class WrapStringLiteral: public CppQuickFixOperation
779
780
{
public:
781
782
    WrapStringLiteral(TextEditor::BaseTextEditor *editor)
        : CppQuickFixOperation(editor), stringLiteral(0), isObjCStringLiteral(false)
783
784
785
786
    {}

    virtual QString description() const
    {
Robert Loehning's avatar
Robert Loehning committed
787
        return QApplication::translate("CppTools::QuickFix", "Enclose in QLatin1String(...)");
788
789
790
791
792
793
794
795
796
797
798
799
800
    }

    virtual int match(const QList<AST *> &path)
    {
        if (path.isEmpty())
            return -1;

        int index = path.size() - 1;
        stringLiteral = path[index]->asStringLiteral();

        if (!stringLiteral)
            return -1;

801
802
        isObjCStringLiteral = charAt(startOf(stringLiteral)) == QLatin1Char('@');

803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
        // check if it is already wrapped in QLatin1String or -Literal
        if (index-2 < 0)
            return index;

        CallAST *call = path[index-1]->asCall();
        PostfixExpressionAST *postfixExp = path[index-2]->asPostfixExpression();
        if (call && postfixExp
            && postfixExp->base_expression
            && postfixExp->postfix_expression_list
            && postfixExp->postfix_expression_list->value == call)
        {
            NameAST *callName = postfixExp->base_expression->asName();
            if (!callName)
                return index;

            QByteArray callNameString(tokenAt(callName->firstToken()).spell());
            if (callNameString == "QLatin1String"
                || callNameString == "QLatin1Literal"
                )
                return -1;
        }

        return index;
    }

828
    virtual void createChanges()
829
    {
830
831
        ChangeSet changes;

832
833
834
835
        const int startPos = startOf(stringLiteral);
        const QLatin1String replacement("QLatin1String(");

        if (isObjCStringLiteral)
836
            changes.replace(startPos, 1, replacement);
837
        else
838
            changes.insert(startPos, replacement);
839

840
841
        changes.insert(endOf(stringLiteral), ")");

842
        refactoringChanges()->changeFile(fileName(), changes);
843
844
845
846
    }

private:
    StringLiteralAST *stringLiteral;
847
    bool isObjCStringLiteral;
848
849
};

850
class CStringToNSString: public CppQuickFixOperation
851
852
{
public:
853
854
    CStringToNSString(TextEditor::BaseTextEditor *editor)
        : CppQuickFixOperation(editor), stringLiteral(0), qlatin1Call(0)
855
856
857
    {}

    virtual QString description() const
Robert Loehning's avatar
Robert Loehning committed
858
859
860
    {
        return QApplication::translate("CppTools::QuickFix", "Convert to Objective-C String Literal");
    }
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902

    virtual int match(const QList<AST *> &path)
    {
        if (path.isEmpty())
            return -1;

        int index = path.size() - 1;
        stringLiteral = path[index]->asStringLiteral();

        if (!stringLiteral)
            return -1;

        if (charAt(startOf(stringLiteral)) == QLatin1Char('@'))
            return -1;

        // check if it is already wrapped in QLatin1String or -Literal
        if (index-2 < 0)
            return index;

        CallAST *call = path[index-1]->asCall();
        PostfixExpressionAST *postfixExp = path[index-2]->asPostfixExpression();
        if (call && postfixExp
            && postfixExp->base_expression
            && postfixExp->postfix_expression_list
            && postfixExp->postfix_expression_list->value == call)
        {
            NameAST *callName = postfixExp->base_expression->asName();
            if (!callName)
                return index;

            if (!(postfixExp->postfix_expression_list->next)) {
                QByteArray callNameString(tokenAt(callName->firstToken()).spell());
                if (callNameString == "QLatin1String"
                    || callNameString == "QLatin1Literal"
                    )
                    qlatin1Call = postfixExp;
            }
        }

        return index;
    }

903
    virtual void createChanges()
904
    {
905
906
        ChangeSet changes;

907
        if (qlatin1Call) {
908
909
910
911
912
            changes.replace(startOf(qlatin1Call),
                            startOf(stringLiteral) - startOf(qlatin1Call),
                            QLatin1String("@"));
            changes.remove(endOf(stringLiteral),
                           endOf(qlatin1Call) - endOf(stringLiteral));
913
        } else {
914
            changes.insert(startOf(stringLiteral), "@");
915
        }
916

917
        refactoringChanges()->changeFile(fileName(), changes);
918
919
920
921
922
923
924
    }

private:
    StringLiteralAST *stringLiteral;
    PostfixExpressionAST *qlatin1Call;
};

925
926
927
} // end of anonymous namespace


928
CppQuickFixOperation::CppQuickFixOperation(TextEditor::BaseTextEditor *editor)
929
930
931
    : TextEditor::QuickFixOperation(editor)
    , _refactoringChanges(0)
    , _topLevelNode(0)
932
933
{ }

934
935
CppQuickFixOperation::~CppQuickFixOperation()
{
936
937
    if (_refactoringChanges)
        delete _refactoringChanges;
938
}
Roberto Raggi's avatar
Roberto Raggi committed
939

940
int CppQuickFixOperation::match(TextEditor::QuickFixState *state)
941
{
942
    CppQuickFixState *s = static_cast<CppQuickFixState *>(state);
943
    _document = s->info.doc;
944
945
946
    if (_refactoringChanges)
        delete _refactoringChanges;
    CPPEditor *cppEditor = qobject_cast<CPPEditor*>(editor());
Roberto Raggi's avatar
Roberto Raggi committed
947
    _refactoringChanges = new CppRefactoringChanges(s->info.snapshot, cppEditor->modelManager());
948
    return match(s->path);
949
}
Roberto Raggi's avatar
Roberto Raggi committed
950

951
952
953
QString CppQuickFixOperation::fileName() const
{ return document()->fileName(); }

954
955
void CppQuickFixOperation::apply()
{
956
    refactoringChanges()->apply();
957
958
}

959
CppEditor::CppRefactoringChanges *CppQuickFixOperation::refactoringChanges() const
960
961
{ return _refactoringChanges; }

962
963
Document::Ptr CppQuickFixOperation::document() const
{ return _document; }
964

965
const Snapshot &CppQuickFixOperation::snapshot() const
966
967
968
{
    return _refactoringChanges->snapshot();
}
969

970
const CPlusPlus::Token &CppQuickFixOperation::tokenAt(unsigned index) const
Roberto Raggi's avatar
Roberto Raggi committed
971
{ return _document->translationUnit()->tokenAt(index); }
972

973
int CppQuickFixOperation::startOf(unsigned index) const
974
{
975
    unsigned line, column;
Roberto Raggi's avatar
Roberto Raggi committed
976
    _document->translationUnit()->getPosition(tokenAt(index).begin(), &line, &column);
977
    return editor()->document()->findBlockByNumber(line - 1).position() + column - 1;
978
}
979

980
int CppQuickFixOperation::startOf(const CPlusPlus::AST *ast) const
981
982
983
984
{
    return startOf(ast->firstToken());
}

985
int CppQuickFixOperation::endOf(unsigned index) const
986
987
{
    unsigned line, column;
Roberto Raggi's avatar
Roberto Raggi committed
988
    _document->translationUnit()->getPosition(tokenAt(index).end(), &line, &column);
989
    return editor()->document()->findBlockByNumber(line - 1).position() + column - 1;
990
991
}

992
int CppQuickFixOperation::endOf(const CPlusPlus::AST *ast) const
993
{
994
995
996
997
    if (unsigned end = ast->lastToken())
        return endOf(end - 1);
    else
        return 0;
998
999
}

1000
void CppQuickFixOperation::startAndEndOf(unsigned index, int *start, int *end) const
1001
1002
1003
1004
{
    unsigned line, column;
    CPlusPlus::Token token(tokenAt(index));
    _document->translationUnit()->getPosition(token.begin(), &line, &column);
1005
    *start = editor()->document()->findBlockByNumber(line - 1).position() + column - 1;
1006
1007
1008
    *end = *start + token.length();
}

1009
bool CppQuickFixOperation::isCursorOn(unsigned tokenIndex) const
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
{
    QTextCursor tc = textCursor();
    int cursorBegin = tc.selectionStart();

    int start = startOf(tokenIndex);
    int end = endOf(tokenIndex);

    if (cursorBegin >= start && cursorBegin <= end)
        return true;

    return false;
}

1023
bool CppQuickFixOperation::isCursorOn(const CPlusPlus::AST *ast) const
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
{
    QTextCursor tc = textCursor();
    int cursorBegin = tc.selectionStart();

    int start = startOf(ast);
    int end = endOf(ast);

    if (cursorBegin >= start && cursorBegin <= end)
        return true;

    return false;
}

1037
1038
void CppQuickFixOperation::move(ChangeSet *changeSet, unsigned tokenIndex,
                                int to)
1039
{
1040
1041
    Q_ASSERT(changeSet);

1042
1043
    int start, end;
    startAndEndOf(tokenIndex, &start, &end);
1044
    changeSet->move(start, end - start, to);
1045
1046
}

1047
1048
void CppQuickFixOperation::move(ChangeSet *changeSet, const CPlusPlus::AST *ast,
                                int to)
1049
{
1050
1051
1052
1053
    Q_ASSERT(changeSet);

    const int start = startOf(ast);
    changeSet->move(start, endOf(ast) - start, to);
1054
1055
}

1056
1057
void CppQuickFixOperation::replace(ChangeSet *changeSet, unsigned tokenIndex,
                                   const QString &replacement)
1058
{
1059
1060
    Q_ASSERT(changeSet);

1061
1062
    int start, end;
    startAndEndOf(tokenIndex, &start, &end);
1063
    changeSet->replace(start, end - start, replacement);
1064
1065
}

1066
1067
1068
void CppQuickFixOperation::replace(ChangeSet *changeSet,
                                   const CPlusPlus::AST *ast,
                                   const QString &replacement)
1069
{
1070
1071
1072
1073
    Q_ASSERT(changeSet);

    const int start = startOf(ast);
    changeSet->replace(start, endOf(ast) - start, replacement);
1074
1075
}

1076
void CppQuickFixOperation::remove(ChangeSet *changeSet, unsigned tokenIndex)
1077
{
1078
1079
    Q_ASSERT(changeSet);

1080
1081
    int start, end;
    startAndEndOf(tokenIndex, &start, &end);
1082
    changeSet->remove(start, end - start);
1083
1084
}

1085
void CppQuickFixOperation::remove(ChangeSet *changeSet, const CPlusPlus::AST *ast)
1086
{
1087
1088
1089
1090
    Q_ASSERT(changeSet);

    const int start = startOf(ast);
    changeSet->remove(start, endOf(ast) - start);
1091
1092
}

1093
1094
1095
void CppQuickFixOperation::flip(ChangeSet *changeSet,
                                const CPlusPlus::AST *ast1,
                                const CPlusPlus::AST *ast2)
Christian Kamm's avatar
Christian Kamm committed
1096
{
1097
1098
1099
1100
1101
1102
    Q_ASSERT(changeSet);

    const int start1 = startOf(ast1);
    const int start2 = startOf(ast2);
    changeSet->flip(start1, endOf(ast1) - start1,
                    start2, endOf(ast2) - start2);
Christian Kamm's avatar
Christian Kamm committed
1103
1104
}

1105
1106
void CppQuickFixOperation::copy(ChangeSet *changeSet, unsigned tokenIndex,
                                int to)
1107
{
1108
1109
    Q_ASSERT(changeSet);

1110
1111
    int start, end;
    startAndEndOf(tokenIndex, &start, &end);
1112
    changeSet->copy(start, end - start, to);
1113
1114
}

1115
1116
void CppQuickFixOperation::copy(ChangeSet *changeSet, const CPlusPlus::AST *ast,
                                int to)
1117
{
1118
1119
1120
1121
    Q_ASSERT(changeSet);

    const int start = startOf(ast);
    changeSet->copy(start, endOf(ast) - start, to);
1122
1123
}

1124
QString CppQuickFixOperation::textOf(const AST *ast) const
1125
{
1126
    return textOf(startOf(ast), endOf(ast));
1127
1128
}

1129
CppQuickFixCollector::CppQuickFixCollector()
1130
1131
{
}
1132

1133
CppQuickFixCollector::~CppQuickFixCollector()
1134
{
1135
}
1136

Roberto Raggi's avatar