cppquickfix.cpp 31.3 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

50
#include <cpptools/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
59

using namespace CppEditor::Internal;
using namespace CPlusPlus;

60
61
namespace {

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

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

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

    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());
123
124
125
126
127
128
129
130
131
132
133
134

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

135
136
137
138
139
        return index;
    }

    virtual void createChangeSet()
    {
140
141
142
143
144
145
146
147
148
        if (negation) {
            // can't remove parentheses since that might break precedence
            remove(negation->unary_op_token);
        } else if (nested) {
            insert(startOf(nested), "!");
        } else {
            insert(startOf(binary), "!(");
            insert(endOf(binary), ")");
        }
149
150
151
152
153
        replace(binary->binary_op_token, replacement);
    }

private:
    BinaryExpressionAST *binary;
154
155
156
    NestedExpressionAST *nested;
    UnaryExpressionAST *negation;

157
158
159
160
161
162
163
164
165
166
    QString replacement;
};

/*
    Rewrite
    a op b

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


    virtual QString description() const
    {
        if (replacement.isEmpty())
Robert Loehning's avatar
Robert Loehning committed
178
            return QApplication::translate("CppTools::QuickFix", "Swap Operands");
179
        else
Robert Loehning's avatar
Robert Loehning committed
180
            return QApplication::translate("CppTools::QuickFix", "Rewrite Using %1").arg(replacement);
181
182
183
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
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
    }

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

    virtual void createChangeSet()
    {
        flip(binary->left_expression, binary->right_expression);
        if (! replacement.isEmpty())
            replace(binary->binary_op_token, replacement);
    }

private:
    BinaryExpressionAST *binary;
    QString replacement;
};

/*
    Rewrite
    !a && !b

    As
    !(a || b)
*/
243
class RewriteLogicalAndOp: public CppQuickFixOperation
244
245
{
public:
246
247
    RewriteLogicalAndOp(TextEditor::BaseTextEditor *editor)
        : CppQuickFixOperation(editor), left(0), right(0), pattern(0)
248
249
250
251
    {}

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

255
    virtual int match(const QList<AST *> &path)
256
    {
257
258
259
260
261
262
263
264
265
266
267
268
        BinaryExpressionAST *expression = 0;

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

        if (! expression)
            return -1;

269
        if (! isCursorOn(expression->binary_op_token))
270
271
            return -1;

272
273
274
275
276
277
278
279
        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)) {
280
            return index;
281
282
        }

283
        return -1;
284
285
    }

Roberto Raggi's avatar
Roberto Raggi committed
286
    virtual void createChangeSet()
287
    {
Roberto Raggi's avatar
Roberto Raggi committed
288
        setTopLevelNode(pattern);
289
        replace(pattern->binary_op_token, QLatin1String("||"));
290
291
292
        remove(left->unary_op_token);
        remove(right->unary_op_token);
        insert(startOf(pattern), QLatin1String("!("));
293
        insert(endOf(pattern), QLatin1String(")"));
294
295
296
297
298
299
300
301
302
303
    }

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

304
class SplitSimpleDeclarationOp: public CppQuickFixOperation
305
306
{
public:
307
308
    SplitSimpleDeclarationOp(TextEditor::BaseTextEditor *editor)
        : CppQuickFixOperation(editor), declaration(0)
309
310
311
312
    {}

    virtual QString description() const
    {
Robert Loehning's avatar
Robert Loehning committed
313
        return QApplication::translate("CppTools::QuickFix", "Split Declaration");
314
315
316
317
    }

    bool checkDeclaration(SimpleDeclarationAST *declaration) const
    {
318
319
320
        if (! declaration->semicolon_token)
            return false;

321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
        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;
    }

343
    virtual int match(const QList<AST *> &path)
344
345
346
347
348
349
350
351
352
353
354
355
356
357
    {
        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;

358
                    const int cursorPosition = selectionStart();
359
360
361
362
363
364
365

                    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.

366
                    if (core_declarator && isCursorOn(core_declarator))
367
                        return index; // got a core-declarator under the text cursor.
368
369
370
371
372
373
374
375
376
                }

                break;
            }
        }

        return -1;
    }

Roberto Raggi's avatar
Roberto Raggi committed
377
    virtual void createChangeSet()
378
    {
Roberto Raggi's avatar
Roberto Raggi committed
379
        setTopLevelNode(declaration);
380
        SpecifierListAST *specifiers = declaration->decl_specifier_list;
381
382
383
        int declSpecifiersStart = startOf(specifiers->firstToken());
        int declSpecifiersEnd = endOf(specifiers->lastToken() - 1);
        int insertPos = endOf(declaration->semicolon_token);
384

385
        DeclaratorAST *prevDeclarator = declaration->declarator_list->value;
386
387
388
389

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

390
391
392
393
394
395
396
            insert(insertPos, QLatin1String("\n"));
            copy(declSpecifiersStart, declSpecifiersEnd, insertPos);
            insert(insertPos, QLatin1String(" "));
            move(declarator, insertPos);
            insert(insertPos, QLatin1String(";"));

            remove(endOf(prevDeclarator), startOf(declarator));
397

398
399
            prevDeclarator = declarator;
        }
400
401
402
403
404
405
    }

private:
    SimpleDeclarationAST *declaration;
};

406
407
408
409
/*
    Add curly braces to a if statement that doesn't already contain a
    compound statement.
*/
410
class AddBracesToIfOp: public CppQuickFixOperation
411
412
{
public:
413
414
    AddBracesToIfOp(TextEditor::BaseTextEditor *editor)
        : CppQuickFixOperation(editor), _statement(0)
415
416
417
418
    {}

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

422
    virtual int match(const QList<AST *> &path)
423
424
425
426
    {
        // show when we're on the 'if' of an if statement
        int index = path.size() - 1;
        IfStatementAST *ifStatement = path.at(index)->asIfStatement();
427
        if (ifStatement && isCursorOn(ifStatement->if_token)
428
429
430
431
432
433
434
435
436
            && ! 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();
437
438
            if (ifStatement && ifStatement->statement
                && isCursorOn(ifStatement->statement)
439
440
441
442
443
444
445
446
447
448
449
450
                && ! ifStatement->statement->asCompoundStatement()) {
                _statement = ifStatement->statement;
                return index;
            }
        }

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

        return -1;
    }

Roberto Raggi's avatar
Roberto Raggi committed
451
    virtual void createChangeSet()
452
    {
Roberto Raggi's avatar
Roberto Raggi committed
453
        setTopLevelNode(_statement);
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
        insert(endOf(_statement->firstToken() - 1), QLatin1String(" {"));
        insert(endOf(_statement->lastToken() - 1), "\n}");
    }

private:
    StatementAST *_statement;
};

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

    With
    Type name = foo;
    if (name) {...}
*/
470
class MoveDeclarationOutOfIfOp: public CppQuickFixOperation
471
472
{
public:
473
474
    MoveDeclarationOutOfIfOp(TextEditor::BaseTextEditor *editor)
        : CppQuickFixOperation(editor), condition(0), pattern(0), core(0)
475
476
477
478
    {}

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

482
    virtual int match(const QList<AST *> &path)
483
484
485
486
487
488
489
490
491
492
493
494
495
    {
        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;

496
                    if (isCursorOn(core))
497
498
499
500
501
502
503
504
                        return index;
                }
            }
        }

        return -1;
    }

Roberto Raggi's avatar
Roberto Raggi committed
505
    virtual void createChangeSet()
506
    {
Roberto Raggi's avatar
Roberto Raggi committed
507
        setTopLevelNode(pattern);
508

509
510
511
512
513
        copy(core, startOf(condition));

        int insertPos = startOf(pattern);
        move(condition, insertPos);
        insert(insertPos, QLatin1String(";\n"));
514
515
516
517
518
519
520
521
522
523
524
    }

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

525
526
527
528
529
530
531
532
/*
    Replace
    while (Type name = foo()) {...}

    With
    Type name;
    while ((name = foo()) != 0) {...}
*/
533
class MoveDeclarationOutOfWhileOp: public CppQuickFixOperation
534
535
{
public:
536
537
    MoveDeclarationOutOfWhileOp(TextEditor::BaseTextEditor *editor)
        : CppQuickFixOperation(editor), condition(0), pattern(0), core(0)
538
539
540
541
    {}

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

545
    virtual int match(const QList<AST *> &path)
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
    {
        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;

566
                    if (isCursorOn(core))
567
568
569
570
571
572
573
574
                        return index;
                }
            }
        }

        return -1;
    }

Roberto Raggi's avatar
Roberto Raggi committed
575
    virtual void createChangeSet()
576
    {
Roberto Raggi's avatar
Roberto Raggi committed
577
        setTopLevelNode(pattern);
578
579
580
581
582
583
584
585

        insert(startOf(condition), QLatin1String("("));
        insert(endOf(condition), QLatin1String(") != 0"));

        int insertPos = startOf(pattern);
        move(startOf(condition), startOf(core), insertPos);
        copy(core, insertPos);
        insert(insertPos, QLatin1String(";\n"));
586
587
588
589
590
591
592
593
594
595
596
    }

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

Roberto Raggi's avatar
Roberto Raggi committed
597
598
599
600
601
602
603
604
605
606
/*
  Replace
     if (something && something_else) {
     }

  with
     if (something) {
        if (something_else) {
        }
     }
Roberto Raggi's avatar
Roberto Raggi committed
607
608
609
610
611
612
613
614
615
616

  and
    if (something || something_else)
      x;

  with
    if (something)
      x;
    else if (something_else)
      x;
Roberto Raggi's avatar
Roberto Raggi committed
617
*/
618
class SplitIfStatementOp: public CppQuickFixOperation
Roberto Raggi's avatar
Roberto Raggi committed
619
620
{
public:
621
622
    SplitIfStatementOp(TextEditor::BaseTextEditor *editor)
        : CppQuickFixOperation(editor), condition(0), pattern(0)
Roberto Raggi's avatar
Roberto Raggi committed
623
624
625
626
    {}

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

630
    virtual int match(const QList<AST *> &path)
Roberto Raggi's avatar
Roberto Raggi committed
631
    {
632
        pattern = 0;
Roberto Raggi's avatar
Roberto Raggi committed
633

634
635
636
637
638
639
640
641
642
        int index = path.size() - 1;
        for (; index != -1; --index) {
            AST *node = path.at(index);
            if (IfStatementAST *stmt = node->asIfStatement()) {
                pattern = stmt;
                break;
            }
        }

643
        if (! pattern || ! pattern->statement)
644
645
            return -1;

646
        unsigned splitKind = 0;
647
648
649
650
651
652
653
        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);
654
655
656
657
658
659
660
661
662
663

            // 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()) {
664
665
                return -1;
            }
666
667
668

            if (isCursorOn(condition->binary_op_token))
                return index;
669
670
671
        }

        return -1;
Roberto Raggi's avatar
Roberto Raggi committed
672
673
    }

Roberto Raggi's avatar
Roberto Raggi committed
674
    virtual void createChangeSet()
Roberto Raggi's avatar
Roberto Raggi committed
675
    {
676
        Token binaryToken = tokenAt(condition->binary_op_token);
Roberto Raggi's avatar
Roberto Raggi committed
677

678
        if (binaryToken.is(T_AMPER_AMPER))
Roberto Raggi's avatar
Roberto Raggi committed
679
680
681
682
683
684
685
            splitAndCondition();
        else
            splitOrCondition();
    }

    void splitAndCondition()
    {
Roberto Raggi's avatar
Roberto Raggi committed
686
        setTopLevelNode(pattern);
Roberto Raggi's avatar
Roberto Raggi committed
687

688
689
690
691
692
693
694
        int startPos = startOf(pattern);
        insert(startPos, QLatin1String("if ("));
        move(condition->left_expression, startPos);
        insert(startPos, QLatin1String(") {\n"));

        remove(endOf(condition->left_expression), startOf(condition->right_expression));
        insert(endOf(pattern), QLatin1String("\n}"));
Roberto Raggi's avatar
Roberto Raggi committed
695
696
    }

Roberto Raggi's avatar
Roberto Raggi committed
697
698
699
700
701
    void splitOrCondition()
    {
        StatementAST *ifTrueStatement = pattern->statement;
        CompoundStatementAST *compoundStatement = ifTrueStatement->asCompoundStatement();

702
        setTopLevelNode(pattern);
Roberto Raggi's avatar
Roberto Raggi committed
703

704
        int insertPos = endOf(ifTrueStatement);
Roberto Raggi's avatar
Roberto Raggi committed
705
        if (compoundStatement)
706
            insert(insertPos, QLatin1String(" "));
Roberto Raggi's avatar
Roberto Raggi committed
707
        else
708
709
710
711
712
            insert(insertPos, QLatin1String("\n"));
        insert(insertPos, QLatin1String("else if ("));
        move(startOf(condition->right_expression), startOf(pattern->rparen_token), insertPos);
        insert(insertPos, QLatin1String(")"));
        copy(endOf(pattern->rparen_token), endOf(pattern->statement), insertPos);
713

714
        remove(endOf(condition->left_expression), startOf(condition->right_expression));
Roberto Raggi's avatar
Roberto Raggi committed
715
716
    }

Roberto Raggi's avatar
Roberto Raggi committed
717
718
719
720
private:
    BinaryExpressionAST *condition;
    IfStatementAST *pattern;
};
721

722
723
724
725
726
727
/*
  Replace
    "abcd"
  With
    QLatin1String("abcd")
*/
728
class WrapStringLiteral: public CppQuickFixOperation
729
730
{
public:
731
732
    WrapStringLiteral(TextEditor::BaseTextEditor *editor)
        : CppQuickFixOperation(editor), stringLiteral(0), isObjCStringLiteral(false)
733
734
735
736
    {}

    virtual QString description() const
    {
Robert Loehning's avatar
Robert Loehning committed
737
        return QApplication::translate("CppTools::QuickFix", "Enclose in QLatin1String(...)");
738
739
740
741
742
743
744
745
746
747
748
749
750
    }

    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;

751
752
        isObjCStringLiteral = charAt(startOf(stringLiteral)) == QLatin1Char('@');

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

    virtual void createChangeSet()
    {
780
781
782
783
784
785
786
787
        const int startPos = startOf(stringLiteral);
        const QLatin1String replacement("QLatin1String(");

        if (isObjCStringLiteral)
            replace(startPos, startPos + 1, replacement);
        else
            insert(startPos, replacement);

788
789
790
791
792
        insert(endOf(stringLiteral), ")");
    }

private:
    StringLiteralAST *stringLiteral;
793
    bool isObjCStringLiteral;
794
795
};

796
class CStringToNSString: public CppQuickFixOperation
797
798
{
public:
799
800
    CStringToNSString(TextEditor::BaseTextEditor *editor)
        : CppQuickFixOperation(editor), stringLiteral(0), qlatin1Call(0)
801
802
803
    {}

    virtual QString description() const
Robert Loehning's avatar
Robert Loehning committed
804
805
806
    {
        return QApplication::translate("CppTools::QuickFix", "Convert to Objective-C String Literal");
    }
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863

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

    virtual void createChangeSet()
    {
        if (qlatin1Call) {
            replace(startOf(qlatin1Call), startOf(stringLiteral), QLatin1String("@"));
            remove(endOf(stringLiteral), endOf(qlatin1Call));
        } else {
            insert(startOf(stringLiteral), "@");
        }
    }

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

864
865
866
} // end of anonymous namespace


867
CppQuickFixOperation::CppQuickFixOperation(TextEditor::BaseTextEditor *editor)
868
869
870
    : TextEditor::QuickFixOperation(editor)
    , _refactoringChanges(0)
    , _topLevelNode(0)
871
872
{ }

873
874
CppQuickFixOperation::~CppQuickFixOperation()
{
875
876
    if (_refactoringChanges)
        delete _refactoringChanges;
877
}
Roberto Raggi's avatar
Roberto Raggi committed
878

879
int CppQuickFixOperation::match(TextEditor::QuickFixState *state)
880
{
881
    CppQuickFixState *s = static_cast<CppQuickFixState *>(state);
882
    _document = s->info.doc;
883
884
885
886
    if (_refactoringChanges)
        delete _refactoringChanges;
    CPPEditor *cppEditor = qobject_cast<CPPEditor*>(editor());
    _refactoringChanges = new CppTools::CppRefactoringChanges(s->info.snapshot, cppEditor->modelManager());
887
    return match(s->path);
888
}
Roberto Raggi's avatar
Roberto Raggi committed
889

890
891
892
893
894
895
896
897
898
899
900
void CppQuickFixOperation::apply()
{
    cppRefactoringChanges()->apply();
}

CppTools::CppRefactoringChanges *CppQuickFixOperation::cppRefactoringChanges() const
{ return _refactoringChanges; }

TextEditor::RefactoringChanges *CppQuickFixOperation::refactoringChanges() const
{ return cppRefactoringChanges(); }

901
902
CPlusPlus::AST *CppQuickFixOperation::topLevelNode() const
{ return _topLevelNode; }
Roberto Raggi's avatar
Roberto Raggi committed
903

904
905
void CppQuickFixOperation::setTopLevelNode(CPlusPlus::AST *topLevelNode)
{ _topLevelNode = topLevelNode; }
Roberto Raggi's avatar
Roberto Raggi committed
906

907
908
Document::Ptr CppQuickFixOperation::document() const
{ return _document; }
909

910
const Snapshot &CppQuickFixOperation::snapshot() const
911
912
913
{
    return _refactoringChanges->snapshot();
}
914

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

918
int CppQuickFixOperation::startOf(unsigned index) const
919
{
920
    unsigned line, column;
Roberto Raggi's avatar
Roberto Raggi committed
921
    _document->translationUnit()->getPosition(tokenAt(index).begin(), &line, &column);
922
    return editor()->document()->findBlockByNumber(line - 1).position() + column - 1;
923
}
924

925
int CppQuickFixOperation::startOf(const CPlusPlus::AST *ast) const
926
927
928
929
{
    return startOf(ast->firstToken());
}

930
int CppQuickFixOperation::endOf(unsigned index) const
931
932
{
    unsigned line, column;
Roberto Raggi's avatar
Roberto Raggi committed
933
    _document->translationUnit()->getPosition(tokenAt(index).end(), &line, &column);
934
    return editor()->document()->findBlockByNumber(line - 1).position() + column - 1;
935
936
}

937
int CppQuickFixOperation::endOf(const CPlusPlus::AST *ast) const
938
{
939
940
941
942
    if (unsigned end = ast->lastToken())
        return endOf(end - 1);
    else
        return 0;
943
944
}

945
void CppQuickFixOperation::startAndEndOf(unsigned index, int *start, int *end) const
946
947
948
949
{
    unsigned line, column;
    CPlusPlus::Token token(tokenAt(index));
    _document->translationUnit()->getPosition(token.begin(), &line, &column);
950
    *start = editor()->document()->findBlockByNumber(line - 1).position() + column - 1;
951
952
953
    *end = *start + token.length();
}

954
bool CppQuickFixOperation::isCursorOn(unsigned tokenIndex) const
955
956
957
958
959
960
961
962
963
964
965
966
967
{
    QTextCursor tc = textCursor();
    int cursorBegin = tc.selectionStart();

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

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

    return false;
}

968
bool CppQuickFixOperation::isCursorOn(const CPlusPlus::AST *ast) const
969
970
971
972
973
974
975
976
977
978
979
980
981
{
    QTextCursor tc = textCursor();
    int cursorBegin = tc.selectionStart();

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

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

    return false;
}

982
void CppQuickFixOperation::move(unsigned tokenIndex, int to)
983
{
984
985
986
    int start, end;
    startAndEndOf(tokenIndex, &start, &end);
    move(start, end, to);
987
988
}

989
void CppQuickFixOperation::move(const CPlusPlus::AST *ast, int to)
990
991
992
993
{
    move(startOf(ast), endOf(ast), to);
}

994
void CppQuickFixOperation::replace(unsigned tokenIndex, const QString &replacement)
995
{
996
997
998
    int start, end;
    startAndEndOf(tokenIndex, &start, &end);
    replace(start, end, replacement);
999
1000
}

1001
void CppQuickFixOperation::replace(const CPlusPlus::AST *ast, const QString &replacement)
1002
1003
1004
1005
{
    replace(startOf(ast), endOf(ast), replacement);
}

1006
void CppQuickFixOperation::remove(unsigned tokenIndex)
1007
{
1008
1009
1010
    int start, end;
    startAndEndOf(tokenIndex, &start, &end);
    remove(start, end);
1011
1012
}

1013
void CppQuickFixOperation::remove(const CPlusPlus::AST *ast)
1014
1015
1016
1017
{
    remove(startOf(ast), endOf(ast));
}

1018
void CppQuickFixOperation::flip(const CPlusPlus::AST *ast1, const CPlusPlus::AST *ast2)
Christian Kamm's avatar
Christian Kamm committed
1019
1020
1021
1022
{
    flip(startOf(ast1), endOf(ast1), startOf(ast2), endOf(ast2));
}

1023
void CppQuickFixOperation::copy(unsigned tokenIndex, int to)
1024
{
1025
1026
1027
    int start, end;
    startAndEndOf(tokenIndex, &start, &end);
    copy(start, end, to);
1028
1029
}

1030
void CppQuickFixOperation::copy(const CPlusPlus::AST *ast, int to)
1031
1032
{
    copy(startOf(ast), endOf(ast), to);
1033
1034
}

1035
QString CppQuickFixOperation::textOf(const AST *ast) const
1036
{
1037
    return textOf(startOf(ast), endOf(ast));
1038
1039
}

1040
CppQuickFixCollector::CppQuickFixCollector()
1041
1042
{
}
1043

1044
CppQuickFixCollector::~CppQuickFixCollector()
1045
{
1046
}
1047

1048
1049
1050
1051
1052
bool CppQuickFixCollector::supportsEditor(TextEditor::ITextEditable *editor)
{
    return CppTools::CppModelManagerInterface::instance()->isCppEditor(editor);
}

1053
1054
1055
1056
TextEditor::QuickFixState *CppQuickFixCollector::initializeCompletion(TextEditor::ITextEditable *editable)
{
    if (CPPEditor *editor = qobject_cast<CPPEditor *>(editable->widget())) {
        const SemanticInfo info = editor->semanticInfo();
1057

1058
1059
1060
1061
        if (info.revision != editor->editorRevision()) {
            // outdated
            qWarning() << "TODO: outdated semantic info, force a reparse.";
            return 0;
1062
        }
Roberto Raggi's avatar
Roberto Raggi committed
1063

1064
1065
        if (info.doc) {
            ASTPath astPath(info.doc);
1066

1067
1068
1069
1070
1071
1072
1073
            const QList<AST *> path = astPath(editor->textCursor());
            if (! path.isEmpty()) {
                CppQuickFixState *state = new CppQuickFixState;
                state->path = path;
                state->info = info;
                return state;
            }
1074
        }
1075
1076
    }

1077
    return 0;
1078
1079
}

1080
QList<TextEditor::QuickFixOperation::Ptr> CppQuickFixCollector::quickFixOperations(TextEditor::BaseTextEditor *editor) const
1081
{
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
    QSharedPointer<RewriteLogicalAndOp> rewriteLogicalAndOp(new RewriteLogicalAndOp(editor));
    QSharedPointer<SplitIfStatementOp> splitIfStatementOp(new SplitIfStatementOp(editor));
    QSharedPointer<MoveDeclarationOutOfIfOp> moveDeclarationOutOfIfOp(new MoveDeclarationOutOfIfOp(editor));
    QSharedPointer<MoveDeclarationOutOfWhileOp> moveDeclarationOutOfWhileOp(new MoveDeclarationOutOfWhileOp(editor));
    QSharedPointer<SplitSimpleDeclarationOp> splitSimpleDeclarationOp(new SplitSimpleDeclarationOp(editor));
    QSharedPointer<AddBracesToIfOp> addBracesToIfOp(new AddBracesToIfOp(editor));
    QSharedPointer<UseInverseOp> useInverseOp(new UseInverseOp(editor));
    QSharedPointer<FlipBinaryOp> flipBinaryOp(new FlipBinaryOp(editor));
    QSharedPointer<WrapStringLiteral> wrapStringLiteral(new WrapStringLiteral(editor));
    QSharedPointer<CStringToNSString> wrapCString(new CStringToNSString(editor));

    QList<TextEditor::QuickFixOperation::Ptr> quickFixOperations;
    quickFixOperations.append(rewriteLogicalAndOp);
    quickFixOperations.append(splitIfStatementOp);
    quickFixOperations.append(moveDeclarationOutOfIfOp);
    quickFixOperations.append(moveDeclarationOutOfWhileOp);
    quickFixOperations.append(splitSimpleDeclarationOp);
    quickFixOperations.append(addBracesToIfOp);
    quickFixOperations.append(useInverseOp);
    quickFixOperations.append(flipBinaryOp);
    quickFixOperations.append(wrapStringLiteral);

    if (editor->mimeType() == CppTools::Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE)
        quickFixOperations.append(wrapCString);

    return quickFixOperations;
1108
}