cppquickfixes.cpp 60.3 KB
Newer Older
1
2
3
4
/**************************************************************************
**
** This file is part of Qt Creator
**
con's avatar
con committed
5
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6
**
7
** Contact: Nokia Corporation (qt-info@nokia.com)
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.
**
con's avatar
con committed
28
** If you have questions regarding the use of this file, please contact
29
** Nokia at qt-info@nokia.com.
30
31
32
**
**************************************************************************/

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

#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>
#include <cplusplus/CppRewriter.h>
#include <cpptools/cpptoolsconstants.h>
61
#include <cpptools/cpprefactoringchanges.h>
62
#include <cpptools/insertionpointlocator.h>
63
#include <cpptools/cpptoolsreuse.h>
64
65
#include <extensionsystem/iplugin.h>

Friedemann Kleint's avatar
Friedemann Kleint committed
66
#include <QtCore/QFileInfo>
67
68
69
70
#include <QtGui/QApplication>
#include <QtGui/QTextBlock>
#include <QtGui/QTextCursor>

71
72
#include <cctype>

73
74
using namespace CppEditor;
using namespace CppEditor::Internal;
75
using namespace CppTools;
76
77
78
79
using namespace TextEditor;
using namespace CPlusPlus;
using namespace Utils;

80
81
82
83
84
static inline bool isQtStringLiteral(const QByteArray &id)
{
    return id == "QLatin1String" || id == "QLatin1Literal" || id == "QStringLiteral";
}

85
86
87
88
89
90
91
namespace {

/*
    Rewrite
    a op b -> !(a invop b)
    (a op b) -> !(a invop b)
    !(a op b) -> (a invob b)
92
93

    Activates on: <= < > >= == !=
94
95
96
97
*/
class UseInverseOp: public CppQuickFixFactory
{
public:
Leandro Melo's avatar
Leandro Melo committed
98
    virtual QList<CppQuickFixOperation::Ptr> match(const QSharedPointer<const CppQuickFixAssistInterface> &interface)
99
100
    {
        QList<CppQuickFixOperation::Ptr> result;
101
        CppRefactoringFilePtr file = interface->currentFile();
102

Leandro Melo's avatar
Leandro Melo committed
103
        const QList<AST *> &path = interface->path();
104
105
106
107
        int index = path.size() - 1;
        BinaryExpressionAST *binary = path.at(index)->asBinaryExpression();
        if (! binary)
            return result;
Leandro Melo's avatar
Leandro Melo committed
108
        if (! interface->isCursorOn(binary->binary_op_token))
109
110
111
            return result;

        Kind invertToken;
112
        switch (file->tokenAt(binary->binary_op_token).kind()) {
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
        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
135
        result.append(CppQuickFixOperation::Ptr(new Operation(interface, index, binary, invertToken)));
136
137
138
139
140
141
142
143
144
145
146
147
148
        return result;
    }

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

        QString replacement;

    public:
Leandro Melo's avatar
Leandro Melo committed
149
150
151
        Operation(const QSharedPointer<const CppQuickFixAssistInterface> &interface,
            int priority, BinaryExpressionAST *binary, Kind invertToken)
            : CppQuickFixOperation(interface, priority)
152
            , binary(binary), nested(0), negation(0)
153
154
155
156
157
158
159
        {
            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
160
                nested = interface->path()[priority - 1]->asNestedExpression();
161
162
163

            // check for ! before parentheses
            if (nested && priority - 2 >= 0) {
Leandro Melo's avatar
Leandro Melo committed
164
                negation = interface->path()[priority - 2]->asUnaryExpression();
165
                if (negation && ! interface->currentFile()->tokenAt(negation->unary_op_token).is(T_EXCLAIM))
166
167
168
169
170
171
172
173
174
                    negation = 0;
            }
        }

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

175
        virtual void performChanges(const CppRefactoringFilePtr &currentFile, const CppRefactoringChanges &)
176
177
178
179
        {
            ChangeSet changes;
            if (negation) {
                // can't remove parentheses since that might break precedence
180
                changes.remove(currentFile->range(negation->unary_op_token));
181
            } else if (nested) {
182
                changes.insert(currentFile->startOf(nested), "!");
183
            } else {
184
185
                changes.insert(currentFile->startOf(binary), "!(");
                changes.insert(currentFile->endOf(binary), ")");
186
            }
187
            changes.replace(currentFile->range(binary->binary_op_token), replacement);
188
189
            currentFile->setChangeSet(changes);
            currentFile->apply();
190
191
192
193
194
195
196
197
198
199
        }
    };
};

/*
    Rewrite
    a op b

    As
    b flipop a
200
201

    Activates on: <= < > >= == != && ||
202
203
204
205
*/
class FlipBinaryOp: public CppQuickFixFactory
{
public:
Leandro Melo's avatar
Leandro Melo committed
206
    virtual QList<QuickFixOperation::Ptr> match(const QSharedPointer<const CppQuickFixAssistInterface> &interface)
207
208
    {
        QList<QuickFixOperation::Ptr> result;
Leandro Melo's avatar
Leandro Melo committed
209
        const QList<AST *> &path = interface->path();
210
        CppRefactoringFilePtr file = interface->currentFile();
211
212
213
214
215

        int index = path.size() - 1;
        BinaryExpressionAST *binary = path.at(index)->asBinaryExpression();
        if (! binary)
            return result;
Leandro Melo's avatar
Leandro Melo committed
216
        if (! interface->isCursorOn(binary->binary_op_token))
217
218
219
            return result;

        Kind flipToken;
220
        switch (file->tokenAt(binary->binary_op_token).kind()) {
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
        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
250
        result.append(QuickFixOperation::Ptr(new Operation(interface, index, binary, replacement)));
251
252
253
254
255
256
257
        return result;
    }

private:
    class Operation: public CppQuickFixOperation
    {
    public:
Leandro Melo's avatar
Leandro Melo committed
258
259
260
        Operation(const QSharedPointer<const CppQuickFixAssistInterface> &interface,
                  int priority, BinaryExpressionAST *binary, QString replacement)
            : CppQuickFixOperation(interface)
261
262
263
264
265
266
267
268
269
270
271
272
273
274
            , 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);
        }

275
        virtual void performChanges(const CppRefactoringFilePtr &currentFile, const CppRefactoringChanges &)
276
277
278
        {
            ChangeSet changes;

279
            changes.flip(currentFile->range(binary->left_expression), currentFile->range(binary->right_expression));
280
            if (! replacement.isEmpty())
281
                changes.replace(currentFile->range(binary->binary_op_token), replacement);
282

283
284
            currentFile->setChangeSet(changes);
            currentFile->apply();
285
286
287
288
289
290
291
292
293
294
295
296
297
298
        }

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

/*
    Rewrite
    !a && !b

    As
    !(a || b)
299
300

    Activates on: &&
301
302
303
304
*/
class RewriteLogicalAndOp: public CppQuickFixFactory
{
public:
Leandro Melo's avatar
Leandro Melo committed
305
    virtual QList<QuickFixOperation::Ptr> match(const QSharedPointer<const CppQuickFixAssistInterface> &interface)
306
307
308
    {
        QList<QuickFixOperation::Ptr> result;
        BinaryExpressionAST *expression = 0;
Leandro Melo's avatar
Leandro Melo committed
309
        const QList<AST *> &path = interface->path();
310
        CppRefactoringFilePtr file = interface->currentFile();
311
312
313
314
315
316
317
318
319
320
321

        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
322
        if (! interface->isCursorOn(expression->binary_op_token))
323
324
            return result;

Leandro Melo's avatar
Leandro Melo committed
325
        QSharedPointer<Operation> op(new Operation(interface));
326
327

        if (expression->match(op->pattern, &matcher) &&
328
329
330
                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)) {
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
            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
348
349
        Operation(const QSharedPointer<const CppQuickFixAssistInterface> &interface)
            : CppQuickFixOperation(interface)
350
351
352
353
354
355
356
            , mk(new ASTPatternBuilder)
        {
            left = mk->UnaryExpression();
            right = mk->UnaryExpression();
            pattern = mk->BinaryExpression(left, right);
        }

357
        virtual void performChanges(const CppRefactoringFilePtr &currentFile, const CppRefactoringChanges &)
358
359
        {
            ChangeSet changes;
360
361
362
363
364
            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);
365
366
367
            changes.insert(start, QLatin1String("!("));
            changes.insert(end, QLatin1String(")"));

368
369
370
            currentFile->setChangeSet(changes);
            currentFile->appendIndentRange(currentFile->range(pattern));
            currentFile->apply();
371
372
373
374
375
376
377
        }
    };

private:
    ASTMatcher matcher;
};

378
379
380
381
382
383
384
/*
    Rewrite
    int *a, b;

    As
    int *a;
    int b;
385
386

    Activates on: the type or the variable names.
387
*/
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
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
418
    virtual QList<CppQuickFixOperation::Ptr> match(const QSharedPointer<const CppQuickFixAssistInterface> &interface)
419
420
421
    {
        QList<CppQuickFixOperation::Ptr> result;
        CoreDeclaratorAST *core_declarator = 0;
Leandro Melo's avatar
Leandro Melo committed
422
        const QList<AST *> &path = interface->path();
423
        CppRefactoringFilePtr file = interface->currentFile();
424
425
426
427
428
429
430
431
432
433
434

        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;

435
                    const int cursorPosition = file->cursor().selectionStart();
436

437
438
                    const int startOfDeclSpecifier = file->startOf(declaration->decl_specifier_list->firstToken());
                    const int endOfDeclSpecifier = file->endOf(declaration->decl_specifier_list->lastToken() - 1);
439
440
441

                    if (cursorPosition >= startOfDeclSpecifier && cursorPosition <= endOfDeclSpecifier) {
                        // the AST node under cursor is a specifier.
Leandro Melo's avatar
Leandro Melo committed
442
                        return singleResult(new Operation(interface, index, declaration));
443
444
                    }

Leandro Melo's avatar
Leandro Melo committed
445
                    if (core_declarator && interface->isCursorOn(core_declarator)) {
446
                        // got a core-declarator under the text cursor.
Leandro Melo's avatar
Leandro Melo committed
447
                        return singleResult(new Operation(interface, index, declaration));
448
449
450
451
452
453
454
455
456
457
458
459
460
461
                    }
                }

                break;
            }
        }

        return result;
    }

private:
    class Operation: public CppQuickFixOperation
    {
    public:
Leandro Melo's avatar
Leandro Melo committed
462
463
        Operation(const QSharedPointer<const CppQuickFixAssistInterface> &interface, int priority, SimpleDeclarationAST *decl)
            : CppQuickFixOperation(interface, priority)
464
465
466
467
468
469
            , declaration(decl)
        {
            setDescription(QApplication::translate("CppTools::QuickFix",
                                                   "Split Declaration"));
        }

470
        virtual void performChanges(const CppRefactoringFilePtr &currentFile, const CppRefactoringChanges &)
471
472
473
474
        {
            ChangeSet changes;

            SpecifierListAST *specifiers = declaration->decl_specifier_list;
475
476
477
            int declSpecifiersStart = currentFile->startOf(specifiers->firstToken());
            int declSpecifiersEnd = currentFile->endOf(specifiers->lastToken() - 1);
            int insertPos = currentFile->endOf(declaration->semicolon_token);
478
479
480
481
482
483
484
485
486

            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(" "));
487
                changes.move(currentFile->range(declarator), insertPos);
488
489
                changes.insert(insertPos, QLatin1String(";"));

490
491
                const int prevDeclEnd = currentFile->endOf(prevDeclarator);
                changes.remove(prevDeclEnd, currentFile->startOf(declarator));
492
493
494
495

                prevDeclarator = declarator;
            }

496
497
498
            currentFile->setChangeSet(changes);
            currentFile->appendIndentRange(currentFile->range(declaration));
            currentFile->apply();
499
500
501
502
503
504
505
506
507
        }

    private:
        SimpleDeclarationAST *declaration;
    };
};

/*
    Add curly braces to a if statement that doesn't already contain a
508
509
510
511
512
513
514
515
    compound statement. I.e.

    if (a)
        b;
    becomes
    if (a) {
        b;
    }
516
517

    Activates on: the if
518
519
520
521
*/
class AddBracesToIfOp: public CppQuickFixFactory
{
public:
Leandro Melo's avatar
Leandro Melo committed
522
    virtual QList<CppQuickFixOperation::Ptr> match(const QSharedPointer<const CppQuickFixAssistInterface> &interface)
523
    {
Leandro Melo's avatar
Leandro Melo committed
524
        const QList<AST *> &path = interface->path();
525
526
527
528

        // 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
529
        if (ifStatement && interface->isCursorOn(ifStatement->if_token) && ifStatement->statement
530
            && ! ifStatement->statement->asCompoundStatement()) {
Leandro Melo's avatar
Leandro Melo committed
531
            return singleResult(new Operation(interface, index, ifStatement->statement));
532
533
534
535
536
537
538
        }

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

        // ### 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
555
556
        Operation(const QSharedPointer<const CppQuickFixAssistInterface> &interface, int priority, StatementAST *statement)
            : CppQuickFixOperation(interface, priority)
557
558
559
560
561
562
            , _statement(statement)
        {
            setDescription(QApplication::translate("CppTools::QuickFix",
                                                   "Add Curly Braces"));
        }

563
        virtual void performChanges(const CppRefactoringFilePtr &currentFile, const CppRefactoringChanges &)
564
565
566
        {
            ChangeSet changes;

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

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

573
574
575
            currentFile->setChangeSet(changes);
            currentFile->appendIndentRange(Utils::ChangeSet::Range(start, end));
            currentFile->apply();
576
577
578
579
580
581
582
583
584
585
586
587
588
589
        }

    private:
        StatementAST *_statement;
    };
};

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

    With
    Type name = foo;
    if (name) {...}
590
591

    Activates on: the name of the introduced variable
592
593
594
595
*/
class MoveDeclarationOutOfIfOp: public CppQuickFixFactory
{
public:
Leandro Melo's avatar
Leandro Melo committed
596
    virtual QList<CppQuickFixOperation::Ptr> match(const QSharedPointer<const CppQuickFixAssistInterface> &interface)
597
    {
Leandro Melo's avatar
Leandro Melo committed
598
599
        const QList<AST *> &path = interface->path();
        QSharedPointer<Operation> op(new Operation(interface));
600
601
602
603
604
605
606
607
608
609

        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
610
                    if (interface->isCursorOn(op->core)) {
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
                        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
627
628
        Operation(const QSharedPointer<const CppQuickFixAssistInterface> &interface)
            : CppQuickFixOperation(interface)
629
630
631
632
633
634
635
636
        {
            setDescription(QApplication::translate("CppTools::QuickFix",
                                                   "Move Declaration out of Condition"));

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

637
        virtual void performChanges(const CppRefactoringFilePtr &currentFile, const CppRefactoringChanges &)
638
639
640
        {
            ChangeSet changes;

641
            changes.copy(currentFile->range(core), currentFile->startOf(condition));
642

643
644
            int insertPos = currentFile->startOf(pattern);
            changes.move(currentFile->range(condition), insertPos);
645
646
            changes.insert(insertPos, QLatin1String(";\n"));

647
648
649
            currentFile->setChangeSet(changes);
            currentFile->appendIndentRange(currentFile->range(pattern));
            currentFile->apply();
650
651
652
653
        }

        ASTMatcher matcher;
        ASTPatternBuilder mk;
654
        CPPEditorWidget *editor;
655
656
657
658
659
660
661
662
663
664
665
666
667
        ConditionAST *condition;
        IfStatementAST *pattern;
        CoreDeclaratorAST *core;
    };
};

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

    With
    Type name;
    while ((name = foo()) != 0) {...}
668
669

    Activates on: the name of the introduced variable
670
671
672
673
*/
class MoveDeclarationOutOfWhileOp: public CppQuickFixFactory
{
public:
Leandro Melo's avatar
Leandro Melo committed
674
    virtual QList<CppQuickFixOperation::Ptr> match(const QSharedPointer<const CppQuickFixAssistInterface> &interface)
675
    {
Leandro Melo's avatar
Leandro Melo committed
676
677
        const QList<AST *> &path = interface->path();
        QSharedPointer<Operation> op(new Operation(interface));
678
679
680
681
682
683
684
685
686
687
688

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

689
                    else if (! declarator->equal_token)
690
691
692
693
694
                        return noResult();

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

Leandro Melo's avatar
Leandro Melo committed
695
                    if (interface->isCursorOn(op->core)) {
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
                        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
712
713
        Operation(const QSharedPointer<const CppQuickFixAssistInterface> &interface)
            : CppQuickFixOperation(interface)
714
715
716
717
718
719
720
721
        {
            setDescription(QApplication::translate("CppTools::QuickFix",
                                                   "Move Declaration out of Condition"));

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

722
        virtual void performChanges(const CppRefactoringFilePtr &currentFile, const CppRefactoringChanges &)
723
724
725
        {
            ChangeSet changes;

726
727
            changes.insert(currentFile->startOf(condition), QLatin1String("("));
            changes.insert(currentFile->endOf(condition), QLatin1String(") != 0"));
728

729
730
731
732
            int insertPos = currentFile->startOf(pattern);
            const int conditionStart = currentFile->startOf(condition);
            changes.move(conditionStart, currentFile->startOf(core), insertPos);
            changes.copy(currentFile->range(core), insertPos);
733
734
            changes.insert(insertPos, QLatin1String(";\n"));

735
736
737
            currentFile->setChangeSet(changes);
            currentFile->appendIndentRange(currentFile->range(pattern));
            currentFile->apply();
738
739
740
741
        }

        ASTMatcher matcher;
        ASTPatternBuilder mk;
742
        CPPEditorWidget *editor;
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
        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;
769
770

    Activates on: && or ||
771
772
773
774
*/
class SplitIfStatementOp: public CppQuickFixFactory
{
public:
Leandro Melo's avatar
Leandro Melo committed
775
    virtual QList<CppQuickFixOperation::Ptr> match(const QSharedPointer<const CppQuickFixAssistInterface> &interface)
776
777
    {
        IfStatementAST *pattern = 0;
Leandro Melo's avatar
Leandro Melo committed
778
        const QList<AST *> &path = interface->path();
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798

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

799
            Token binaryToken = interface->currentFile()->tokenAt(condition->binary_op_token);
800
801
802
803
804
805
806
807
808
809
810
811
812

            // 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
813
814
            if (interface->isCursorOn(condition->binary_op_token))
                return singleResult(new Operation(interface, index, pattern, condition));
815
816
817
818
819
820
821
822
823
        }

        return noResult();
    }

private:
    class Operation: public CppQuickFixOperation
    {
    public:
Leandro Melo's avatar
Leandro Melo committed
824
        Operation(const QSharedPointer<const CppQuickFixAssistInterface> &interface, int priority,
825
                  IfStatementAST *pattern, BinaryExpressionAST *condition)
Leandro Melo's avatar
Leandro Melo committed
826
            : CppQuickFixOperation(interface, priority)
827
828
829
830
831
832
833
            , pattern(pattern)
            , condition(condition)
        {
            setDescription(QApplication::translate("CppTools::QuickFix",
                                                   "Split if Statement"));
        }

834
        virtual void performChanges(const CppRefactoringFilePtr &currentFile, const CppRefactoringChanges &)
835
        {
836
            const Token binaryToken = currentFile->tokenAt(condition->binary_op_token);
837
838

            if (binaryToken.is(T_AMPER_AMPER))
839
                splitAndCondition(currentFile);
840
            else
841
                splitOrCondition(currentFile);
842
843
        }

844
        void splitAndCondition(CppRefactoringFilePtr currentFile)
845
846
847
        {
            ChangeSet changes;

848
            int startPos = currentFile->startOf(pattern);
849
            changes.insert(startPos, QLatin1String("if ("));
850
            changes.move(currentFile->range(condition->left_expression), startPos);
851
852
            changes.insert(startPos, QLatin1String(") {\n"));

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

857
858
859
            currentFile->setChangeSet(changes);
            currentFile->appendIndentRange(currentFile->range(pattern));
            currentFile->apply();
860
861
        }

862
        void splitOrCondition(CppRefactoringFilePtr currentFile)
863
864
865
866
867
868
        {
            ChangeSet changes;

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

869
            int insertPos = currentFile->endOf(ifTrueStatement);
870
871
872
873
874
875
            if (compoundStatement)
                changes.insert(insertPos, QLatin1String(" "));
            else
                changes.insert(insertPos, QLatin1String("\n"));
            changes.insert(insertPos, QLatin1String("else if ("));

876
877
            const int rExprStart = currentFile->startOf(condition->right_expression);
            changes.move(rExprStart, currentFile->startOf(pattern->rparen_token), insertPos);
878
879
            changes.insert(insertPos, QLatin1String(")"));

880
881
            const int rParenEnd = currentFile->endOf(pattern->rparen_token);
            changes.copy(rParenEnd, currentFile->endOf(pattern->statement), insertPos);
882

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

886
887
888
            currentFile->setChangeSet(changes);
            currentFile->appendIndentRange(currentFile->range(pattern));
            currentFile->apply();
889
890
891
892
893
894
895
896
897
898
        }

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

/*
  Replace
899
    "abcd" -> QLatin1String("abcd")
900
    @"abcd" -> QLatin1String("abcd")
901
902
903
904
    'a' -> QLatin1Char('a')
  Except if they are already enclosed in
    QLatin1Char, QT_TRANSLATE_NOOP, tr,
    trUtf8, QLatin1Literal, QLatin1String
905
906

    Activates on: the string literal
907
*/
908
909
910
911
912
913
914

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

915
916
917
918
919
class WrapStringLiteral: public CppQuickFixFactory
{
public:
    enum Type { TypeString, TypeObjCString, TypeChar, TypeNone };

Leandro Melo's avatar
Leandro Melo committed
920
    virtual QList<CppQuickFixOperation::Ptr> match(const QSharedPointer<const CppQuickFixAssistInterface> &interface)
921
922
923
    {
        ExpressionAST *literal = 0;
        Type type = TypeNone;
Leandro Melo's avatar
Leandro Melo committed
924
        const QList<AST *> &path = interface->path();
925
        CppRefactoringFilePtr file = interface->currentFile();
926
927
928
929
930
931
932
933

        if (path.isEmpty())
            return noResult(); // nothing to do

        literal = path.last()->asStringLiteral();

        if (! literal) {
            literal = path.last()->asNumericLiteral();
934
            if (!literal || !file->tokenAt(literal->asNumericLiteral()->literal_token).is(T_CHAR_LITERAL))
935
936
937
938
939
940
941
942
943
944
                return noResult();
            else
                type = TypeChar;
        } else {
            type = TypeString;
        }

        if (path.size() > 1) {
            if (CallAST *call = path.at(path.size() - 2)->asCall()) {
                if (call->base_expression) {
Roberto Raggi's avatar
Roberto Raggi committed
945
946
                    if (IdExpressionAST *idExpr = call->base_expression->asIdExpression()) {
                        if (SimpleNameAST *functionName = idExpr->name->asSimpleName()) {
947
                            const QByteArray id(file->tokenAt(functionName->identifier_token).identifier->chars());
Roberto Raggi's avatar
Roberto Raggi committed
948
949

                            if (id == "QT_TRANSLATE_NOOP" || id == "tr" || id == "trUtf8"
950
                                    || (type == TypeString && isQtStringLiteral(id))
Roberto Raggi's avatar
Roberto Raggi committed
951
952
953
                                    || (type == TypeChar && id == "QLatin1Char"))
                                return noResult(); // skip it
                        }
954
955
956
957
958
959
                    }
                }
            }
        }

        if (type == TypeString) {
960
            if (file->charAt(file->startOf(literal)) == QLatin1Char('@'))
961
962
                type = TypeObjCString;
        }
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985

        QList<CppQuickFixOperation::Ptr> result;

        const int priority = path.size() - 1; // very high priority
        if (type == TypeChar) {
            const QString replacement = QLatin1String("QLatin1Char");
            const QString description =
                QApplication::translate("CppTools::QuickFix", "Enclose in %1(...)")
                           .arg(replacement);

            result.push_back(CppQuickFixOperation::Ptr(new Operation(interface, priority, type,
                                                       replacement, description, literal)));
        } else {
            QString replacement = QLatin1String("QLatin1String");
            result.push_back(CppQuickFixOperation::Ptr(new Operation(interface, priority, type,
                                                                     replacement, msgQtStringLiteralDescription(replacement, 4),
                                                                     literal)));
            replacement = QLatin1String("QStringLiteral");
            result.push_back(CppQuickFixOperation::Ptr(new Operation(interface, priority, type,
                                                                     replacement, msgQtStringLiteralDescription(replacement, 5),
                                                                     literal)));
        }
        return result;
986
987
988
989
990
991
    }

private:
    class Operation: public CppQuickFixOperation
    {
    public:
992
993
        Operation(const QSharedPointer<const CppQuickFixAssistInterface> &interface, int priority,
                  Type type, const QString &replacement, const QString &description,
994
                  ExpressionAST *literal)
Leandro Melo's avatar
Leandro Melo committed
995
            : CppQuickFixOperation(interface, priority)
996
            , type(type), replacement(replacement)
997
998
            , literal(literal)
        {
999
1000
            setDescription(description);
       }
1001

1002
        virtual void performChanges(const CppRefactoringFilePtr &currentFile, const CppRefactoringChanges &)
1003
1004
1005
        {
            ChangeSet changes;

1006
            const int startPos = currentFile->startOf(literal);
1007
            const QString fullReplacement = replacement + QLatin1Char('(');
1008
            if (type == TypeObjCString)
1009
                changes.replace(startPos, startPos + 1, fullReplacement);
1010
            else
1011
                changes.insert(startPos, fullReplacement);
1012

1013
            changes.insert(currentFile->endOf(literal), QLatin1String(")"));
1014

1015
1016
            currentFile->setChangeSet(changes);
            currentFile->apply();
1017
1018
1019
        }

    private:
1020
1021
        const Type type;
        const QString replacement;
1022
1023
        ExpressionAST *literal;
    };
1024

1025
1026
1027
1028
1029
1030
1031
1032
1033
};

/*
  Replace
    "abcd"
  With
    tr("abcd") or
    QCoreApplication::translate("CONTEXT", "abcd") or
    QT_TRANSLATE_NOOP("GLOBAL", "abcd")
1034
  depending on what is available.
1035
1036

    Activates on: the string literal
1037
1038
1039
1040
1041
1042
*/
class TranslateStringLiteral: public CppQuickFixFactory
{
public:
    enum TranslationOption { unknown, useTr, useQCoreApplicationTranslate, useMacro };

Leandro Melo's avatar
Leandro Melo committed
1043
    virtual QList<CppQuickFixOperation::Ptr> match(const QSharedPointer<const CppQuickFixAssistInterface> &interface)
1044
    {
Leandro Melo's avatar
Leandro Melo committed
1045
        const QList<AST *> &path = interface->path();
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
        // Initialize
        ExpressionAST *literal = 0;
        QString trContext;

        if (path.isEmpty())
            return noResult();

        literal = path.last()->asStringLiteral();
        if (!literal)
            return noResult(); // No string, nothing to do

        // Do we already have a translation markup?
        if (path.size() >= 2) {
            if (CallAST *call = path.at(path.size() - 2)->asCall()) {
                if (call->base_expression) {
Roberto Raggi's avatar
Roberto Raggi committed
1061
1062
                    if (IdExpressionAST *idExpr = call->base_expression->asIdExpression()) {
                        if (SimpleNameAST *functionName = idExpr->name->asSimpleName()) {
1063
                            const QByteArray id(interface->currentFile()->tokenAt(functionName->identifier_token).identifier->chars());
Roberto Raggi's avatar
Roberto Raggi committed
1064
1065
1066
1067

                            if (id == "tr" || id == "trUtf8"
                                    || id == "translate"
                                    || id == "QT_TRANSLATE_NOOP"
1068
                                    || isQtStringLiteral(id))
Roberto Raggi's avatar
Roberto Raggi committed
1069
1070
                                return noResult(); // skip it
                        }
1071
1072
1073
1074
1075
                    }
                }
            }
        }

Leandro Melo's avatar
Leandro Melo committed
1076
        QSharedPointer<Control> control = interface->context().control();
1077
        const Name *trName = control->identifier("tr");
1078
1079
1080
1081
1082
1083

        // Check whether we are in a method:
        for (int i = path.size() - 1; i >= 0; --i)
        {
            if (FunctionDefinitionAST *definition = path.at(i)->asFunctionDefinition()) {
                Function *function = definition->symbol;
Leandro Melo's avatar
Leandro Melo committed
1084
                ClassOrNamespace *b = interface->context().lookupType(function);
1085
1086
1087
1088
1089
1090
                if (b) {
                    // Do we have a tr method?
                    foreach(const LookupItem &r, b->find(trName)) {
                        Symbol *s = r.declaration();
                        if (s->type()->isFunctionType()) {
                            // no context required for tr
Leandro Melo's avatar
Leandro Melo committed
1091
                            return singleResult(new Operation(interface, path.size() - 1, literal, useTr, trContext));
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
                        }
                    }
                }
                // We need to do a QCA::translate, so we need a context.
                // Use fully qualified class name:
                Overview oo;
                foreach (const Name *n, LookupContext::path(function)) {
                    if (!trContext.isEmpty())
                        trContext.append(QLatin1String("::"));
                    trContext.append(oo.prettyName(n));
                }
                // ... or global if none available!
                if (trContext.isEmpty())
                    trContext = QLatin1String("GLOBAL");
Leandro Melo's avatar
Leandro Melo committed
1106
                return singleResult(new Operation(interface, path.size() - 1, literal, useQCoreApplicationTranslate, trContext));
1107
1108
1109
1110
            }
        }

        // We need to use Q_TRANSLATE_NOOP
Leandro Melo's avatar
Leandro Melo committed
1111
        return singleResult(new Operation(interface, path.size() - 1, literal, useMacro, QLatin1String("GLOBAL")));
1112
1113
1114
1115
1116
1117
    }

private:
    class Operation: public CppQuickFixOperation
    {
    public:
Leandro Melo's avatar
Leandro Melo committed
1118
1119
        Operation(const QSharedPointer<const CppQuickFixAssistInterface> &interface, int priority, ExpressionAST *literal, TranslationOption option, const QString &context)
            : CppQuickFixOperation(interface, priority)
1120
1121
1122
1123
            , m_literal(literal)
            , m_option(option)
            , m_context(context)
        {
1124
            setDescription(QApplication::translate("CppTools::QuickFix", "Mark as Translatable"));
1125
1126
        }

1127
        virtual void performChanges(const CppRefactoringFilePtr &currentFile, const CppRefactoringChanges &)
1128
1129
1130
        {
            ChangeSet changes;

1131
            const int startPos = currentFile->startOf(m_literal);
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
            QString replacement(QLatin1String("tr("));
            if (m_option == useQCoreApplicationTranslate) {
                replacement = QLatin1String("QCoreApplication::translate(\"")
                        + m_context + QLatin1String("\", ");
            } else if (m_option == useMacro) {
                replacement = QLatin1String("QT_TRANSLATE_NOOP(\"")
                        + m_context + QLatin1String("\", ");
            }

            changes.insert(startPos, replacement);
1142
            changes.insert(currentFile->endOf(m_literal), QLatin1String(")"));
1143

1144
1145
            currentFile->setChangeSet(changes);
            currentFile->apply();
1146
1147
1148
1149
1150
1151
1152
1153
1154
        }

    private:
        ExpressionAST *m_literal;
        TranslationOption m_option;
        QString m_context;
    };
};

1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
/**
 * Replace
 *    "abcd"
 *    QLatin1String("abcd")
 *    QLatin1Literal("abcd")
 * With
 *    @"abcd"
 *
 * Activates on: the string literal, if the file type is a Objective-C(++) file.
 */
1165
1166
1167
class CStringToNSString: public CppQuickFixFactory
{
public:
Leandro Melo's avatar
Leandro Melo committed
1168
    virtual QList<CppQuickFixOperation::Ptr> match(const QSharedPointer<const CppQuickFixAssistInterface> &interface)
1169
    {
1170
        CppRefactoringFilePtr file = interface->currentFile();
1171

Leandro Melo's avatar
Leandro Melo committed
1172
        if (interface->editor()->mimeType() != CppTools::Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE)
1173
1174
1175
1176
            return noResult();

        StringLiteralAST *stringLiteral = 0;
        CallAST *qlatin1Call = 0;
Leandro Melo's avatar
Leandro Melo committed
1177
        const QList<AST *> &path = interface->path();
1178
1179
1180
1181
1182
1183
1184
1185
1186

        if (path.isEmpty())
            return noResult(); // nothing to do

        stringLiteral = path.last()->asStringLiteral();

        if (! stringLiteral)
            return noResult();

1187
        else if (file->charAt(file->startOf(stringLiteral)) == QLatin1Char('@'))
1188
1189
1190
1191
1192
            return noResult(); // it's already an objc string literal.

        else if (path.size() > 1) {
            if (CallAST *call = path.at(path.size() - 2)->asCall()) {
                if (call->base_expression) {
Roberto Raggi's avatar
Roberto Raggi committed
1193
1194
                    if (IdExpressionAST *idExpr = call->base_expression->asIdExpression()) {
                        if (SimpleNameAST *functionName = idExpr->name->asSimpleName()) {
1195
                            const QByteArray id(interface->currentFile()->tokenAt(functionName->identifier_token).identifier->chars());
1196

1197
                            if (isQtStringLiteral(id))
Roberto Raggi's avatar
Roberto Raggi committed
1198
1199
                                qlatin1Call = call;
                        }
1200
1201
1202
1203
1204
                    }
                }
            }
        }

Leandro Melo's avatar
Leandro Melo committed
1205
        return singleResult(new Operation(interface, path.size() - 1, stringLiteral, qlatin1Call));
1206
1207
1208
1209
1210
1211
    }

private:
    class Operation: public CppQuickFixOperation
    {
    public:
Leandro Melo's avatar
Leandro Melo committed
1212
1213
        Operation(const QSharedPointer<const CppQuickFixAssistInterface> &interface, int priority, StringLiteralAST *stringLiteral, CallAST *qlatin1Call)
            : CppQuickFixOperation(interface, priority)
1214
1215
1216
1217
1218
1219
1220
            , stringLiteral(stringLiteral)
            , qlatin1Call(qlatin1Call)
        {
            setDescription(QApplication::translate("CppTools::QuickFix",
                                                   "Convert to Objective-C String Literal"));
        }

1221
        virtual void performChanges(const CppRefactoringFilePtr &currentFile, const CppRefactoringChanges &)
1222
1223
1224
1225
        {
            ChangeSet changes;

            if (qlatin1Call) {
1226
1227
                changes.replace(currentFile->startOf(qlatin1Call), currentFile->startOf(stringLiteral), QLatin1String("@"));
                changes.remove(currentFile->endOf(stringLiteral), currentFile->endOf(qlatin1Call));
1228
            } else {
1229
                changes.insert(currentFile->startOf(stringLiteral), "@");
1230
1231
            }

1232
1233
            currentFile->setChangeSet(changes);
            currentFile->apply();
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
        }

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

/*
  Base class for converting numeric literals between decimal, octal and hex.
  Does the base check for the specific ones and parses the number.
  Test cases:
    0xFA0Bu;
    0X856A;
    298.3;
    199;
    074;
    199L;
    074L;
    -199;
    -017;
    0783; // invalid octal
    0; // border case, allow only hex<->decimal
1257
1258

    Activates on: numeric literals
1259
1260
1261
1262
*/
class ConvertNumericLiteral: public CppQuickFixFactory
{
public:
Leandro Melo's avatar
Leandro Melo committed
1263
    virtual QList<QuickFixOperation::Ptr> match(const QSharedPointer<const CppQuickFixAssistInterface> &interface)
1264
1265
1266
    {
        QList<QuickFixOperation::Ptr> result;

Leandro Melo's avatar
Leandro Melo committed
1267
        const QList<AST *> &path = interface->path();
1268
        CppRefactoringFilePtr file = interface->currentFile();
1269
1270
1271
1272
1273
1274
1275
1276
1277

        if (path.isEmpty())
            return result; // nothing to do

        NumericLiteralAST *literal = path.last()->asNumericLiteral();

        if (! literal)
            return result;

1278
        Token token = file->tokenAt(literal->asNumericLiteral()->literal_token);
1279
1280
1281
1282
1283
1284
1285
1286
1287
        if (!token.is(T_NUMERIC_LITERAL))
            return result;
        const NumericLiteral *numeric = token.number;
        if (numeric->isDouble() || numeric->isFloat())
            return result;

        // remove trailing L or U and stuff
        const char * const spell = numeric->chars();
        int numberLength = numeric->size();
1288
        while (numberLength > 0 && !std::isxdigit(spell[numberLength - 1]))
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
            --numberLength;
        if (numberLength < 1)
            return result;

        // convert to number
        bool valid;
        ulong value = QString::fromUtf8(spell).left(numberLength).toULong(&valid, 0);
        if (!valid) // e.g. octal with digit > 7
            return result;

        const int priority = path.size() - 1; // very high priority
1300
        const int start = file->startOf(literal);
1301
1302
1303
1304