cppquickfixes.cpp 161 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
2
**
3
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
7
**
hjk's avatar
hjk committed
8
9
10
11
12
13
14
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
15
16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
18
19
20
21
22
23
24
25
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
con's avatar
con committed
26
27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
29

Nikolai Kosjar's avatar
Nikolai Kosjar committed
30
31
#include "cppquickfixes.h"

32
#include "cppeditor.h"
33
#include "cppfunctiondecldeflink.h"
Leandro Melo's avatar
Leandro Melo committed
34
#include "cppquickfixassistant.h"
35

36
#include <cpptools/cppclassesfilter.h>
37
#include <cpptools/cppcodestylesettings.h>
38
39
#include <cpptools/cpppointerdeclarationformatter.h>
#include <cpptools/cpptoolsconstants.h>
40
41
#include <cpptools/cpptoolsreuse.h>
#include <cpptools/insertionpointlocator.h>
42
#include <cpptools/symbolfinder.h>
43

44
#include <cplusplus/ASTPath.h>
45
#include <cplusplus/CPlusPlusForwardDeclarations.h>
46
47
48
49
#include <cplusplus/CppRewriter.h>
#include <cplusplus/DependencyTable.h>
#include <cplusplus/TypeOfExpression.h>

50
#include <extensionsystem/pluginmanager.h>
51

52
53
#include <utils/qtcassert.h>

54
#include <QApplication>
55
#include <QDir>
56
#include <QFileInfo>
57
58
#include <QInputDialog>
#include <QMessageBox>
Nikolai Kosjar's avatar
Nikolai Kosjar committed
59
#include <QSharedPointer>
60
61
#include <QTextBlock>
#include <QTextCursor>
62

Nikolai Kosjar's avatar
Nikolai Kosjar committed
63
64
#include <cctype>

65
using namespace CPlusPlus;
66
67
using namespace CppEditor;
using namespace CppEditor::Internal;
68
69
70
using namespace CppTools;
using namespace TextEditor;
using namespace Utils;
Nikolai Kosjar's avatar
Nikolai Kosjar committed
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106

void CppEditor::Internal::registerQuickFixes(ExtensionSystem::IPlugin *plugIn)
{
    plugIn->addAutoReleasedObject(new AddIncludeForUndefinedIdentifier);
    plugIn->addAutoReleasedObject(new AddIncludeForForwardDeclaration);

    plugIn->addAutoReleasedObject(new FlipLogicalOperands);
    plugIn->addAutoReleasedObject(new InverseLogicalComparison);
    plugIn->addAutoReleasedObject(new RewriteLogicalAnd);

    plugIn->addAutoReleasedObject(new ConvertToCamelCase);

    plugIn->addAutoReleasedObject(new ConvertCStringToNSString);
    plugIn->addAutoReleasedObject(new ConvertNumericLiteral);
    plugIn->addAutoReleasedObject(new TranslateStringLiteral);
    plugIn->addAutoReleasedObject(new WrapStringLiteral);

    plugIn->addAutoReleasedObject(new MoveDeclarationOutOfIf);
    plugIn->addAutoReleasedObject(new MoveDeclarationOutOfWhile);

    plugIn->addAutoReleasedObject(new SplitIfStatement);
    plugIn->addAutoReleasedObject(new SplitSimpleDeclaration);

    plugIn->addAutoReleasedObject(new AddLocalDeclaration);
    plugIn->addAutoReleasedObject(new AddBracesToIf);
    plugIn->addAutoReleasedObject(new RearrangeParamDeclarationList);
    plugIn->addAutoReleasedObject(new ReformatPointerDeclaration);

    plugIn->addAutoReleasedObject(new CompleteSwitchCaseStatement);
    plugIn->addAutoReleasedObject(new InsertQtPropertyMembers);

    plugIn->addAutoReleasedObject(new ApplyDeclDefLinkChanges);
    plugIn->addAutoReleasedObject(new ExtractFunction);
    plugIn->addAutoReleasedObject(new GenerateGetterSetter);
    plugIn->addAutoReleasedObject(new InsertDeclFromDef);
    plugIn->addAutoReleasedObject(new InsertDefFromDecl);
107
108
109

    plugIn->addAutoReleasedObject(new MoveFuncDefOutside);
    plugIn->addAutoReleasedObject(new MoveFuncDefToDecl);
110
111

    plugIn->addAutoReleasedObject(new AssignToLocalVariable);
Nikolai Kosjar's avatar
Nikolai Kosjar committed
112
}
113

114
115
116
117
// In the following anonymous namespace all functions are collected, which could be of interest for
// different quick fixes.
namespace {

118
119
120
121
122
123
enum DefPos {
    DefPosInsideClass,
    DefPosOutsideClass,
    DefPosImplementationFile
};

124
inline bool isQtStringLiteral(const QByteArray &id)
125
126
127
128
{
    return id == "QLatin1String" || id == "QLatin1Literal" || id == "QStringLiteral";
}

129
inline bool isQtStringTranslation(const QByteArray &id)
130
131
132
133
{
    return id == "tr" || id == "trUtf8" || id == "translate" || id == "QT_TRANSLATE_NOOP";
}

134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
Class *isMemberFunction(const LookupContext &context, Function *function)
{
    QTC_ASSERT(function, return 0);

    Scope *enclosingScope = function->enclosingScope();
    while (! (enclosingScope->isNamespace() || enclosingScope->isClass()))
        enclosingScope = enclosingScope->enclosingScope();
    QTC_ASSERT(enclosingScope != 0, return 0);

    const Name *functionName = function->name();
    if (! functionName)
        return 0; // anonymous function names are not valid c++

    if (! functionName->isQualifiedNameId())
        return 0; // trying to add a declaration for a global function

    const QualifiedNameId *q = functionName->asQualifiedNameId();
    if (!q->base())
        return 0;

    if (ClassOrNamespace *binding = context.lookupType(q->base(), enclosingScope)) {
        foreach (Symbol *s, binding->symbols()) {
            if (Class *matchingClass = s->asClass())
                return matchingClass;
        }
    }

    return 0;
}

} // anonymous namespace

namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
168
class InverseLogicalComparisonOp: public CppQuickFixOperation
169
170
{
public:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
171
172
173
174
    InverseLogicalComparisonOp(const CppQuickFixInterface &interface, int priority,
                               BinaryExpressionAST *binary, Kind invertToken)
        : CppQuickFixOperation(interface, priority)
        , binary(binary), nested(0), negation(0)
175
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
176
177
178
        Token tok;
        tok.f.kind = invertToken;
        replacement = QLatin1String(tok.spell());
179

Nikolai Kosjar's avatar
Nikolai Kosjar committed
180
181
182
        // check for enclosing nested expression
        if (priority - 1 >= 0)
            nested = interface->path()[priority - 1]->asNestedExpression();
183

Nikolai Kosjar's avatar
Nikolai Kosjar committed
184
185
186
187
188
        // check for ! before parentheses
        if (nested && priority - 2 >= 0) {
            negation = interface->path()[priority - 2]->asUnaryExpression();
            if (negation && ! interface->currentFile()->tokenAt(negation->unary_op_token).is(T_EXCLAIM))
                negation = 0;
189
190
191
        }
    }

Nikolai Kosjar's avatar
Nikolai Kosjar committed
192
    QString description() const
193
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
194
195
        return QApplication::translate("CppTools::QuickFix", "Rewrite Using %1").arg(replacement);
    }
196

Nikolai Kosjar's avatar
Nikolai Kosjar committed
197
198
199
200
201
202
203
204
205
206
207
208
209
210
    void perform()
    {
        CppRefactoringChanges refactoring(snapshot());
        CppRefactoringFilePtr currentFile = refactoring.file(fileName());

        ChangeSet changes;
        if (negation) {
            // can't remove parentheses since that might break precedence
            changes.remove(currentFile->range(negation->unary_op_token));
        } else if (nested) {
            changes.insert(currentFile->startOf(nested), QLatin1String("!"));
        } else {
            changes.insert(currentFile->startOf(binary), QLatin1String("!("));
            changes.insert(currentFile->endOf(binary), QLatin1String(")"));
211
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
212
213
214
215
        changes.replace(currentFile->range(binary->binary_op_token), replacement);
        currentFile->setChangeSet(changes);
        currentFile->apply();
    }
216

Nikolai Kosjar's avatar
Nikolai Kosjar committed
217
218
219
220
private:
    BinaryExpressionAST *binary;
    NestedExpressionAST *nested;
    UnaryExpressionAST *negation;
221

Nikolai Kosjar's avatar
Nikolai Kosjar committed
222
    QString replacement;
223
224
};

225
226
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
void InverseLogicalComparison::match(const CppQuickFixInterface &interface,
                                     QuickFixOperations &result)
{
    CppRefactoringFilePtr file = interface->currentFile();

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

    Kind invertToken;
    switch (file->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;
    }
263

Nikolai Kosjar's avatar
Nikolai Kosjar committed
264
265
266
    result.append(CppQuickFixOperation::Ptr(
        new InverseLogicalComparisonOp(interface, index, binary, invertToken)));
}
267

268
269
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
270
class FlipLogicalOperandsOp: public CppQuickFixOperation
271
272
{
public:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
273
274
275
276
277
    FlipLogicalOperandsOp(const CppQuickFixInterface &interface, int priority,
                          BinaryExpressionAST *binary, QString replacement)
        : CppQuickFixOperation(interface)
        , binary(binary)
        , replacement(replacement)
278
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
279
280
        setPriority(priority);
    }
281

Nikolai Kosjar's avatar
Nikolai Kosjar committed
282
283
284
285
286
287
288
    QString description() const
    {
        if (replacement.isEmpty())
            return QApplication::translate("CppTools::QuickFix", "Swap Operands");
        else
            return QApplication::translate("CppTools::QuickFix", "Rewrite Using %1").arg(replacement);
    }
289

Nikolai Kosjar's avatar
Nikolai Kosjar committed
290
291
292
293
    void perform()
    {
        CppRefactoringChanges refactoring(snapshot());
        CppRefactoringFilePtr currentFile = refactoring.file(fileName());
294

Nikolai Kosjar's avatar
Nikolai Kosjar committed
295
        ChangeSet changes;
296
297
        changes.flip(currentFile->range(binary->left_expression),
                     currentFile->range(binary->right_expression));
Nikolai Kosjar's avatar
Nikolai Kosjar committed
298
299
        if (! replacement.isEmpty())
            changes.replace(currentFile->range(binary->binary_op_token), replacement);
300

Nikolai Kosjar's avatar
Nikolai Kosjar committed
301
302
        currentFile->setChangeSet(changes);
        currentFile->apply();
303
304
305
    }

private:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
306
307
308
    BinaryExpressionAST *binary;
    QString replacement;
};
309

310
311
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
312
313
314
315
void FlipLogicalOperands::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
{
    const QList<AST *> &path = interface->path();
    CppRefactoringFilePtr file = interface->currentFile();
316

Nikolai Kosjar's avatar
Nikolai Kosjar committed
317
318
319
320
321
322
    int index = path.size() - 1;
    BinaryExpressionAST *binary = path.at(index)->asBinaryExpression();
    if (! binary)
        return;
    if (! interface->isCursorOn(binary->binary_op_token))
        return;
323

Nikolai Kosjar's avatar
Nikolai Kosjar committed
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
    Kind flipToken;
    switch (file->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;
    }
347

Nikolai Kosjar's avatar
Nikolai Kosjar committed
348
349
350
351
352
353
    QString replacement;
    if (flipToken != T_EOF_SYMBOL) {
        Token tok;
        tok.f.kind = flipToken;
        replacement = QLatin1String(tok.spell());
    }
354

Nikolai Kosjar's avatar
Nikolai Kosjar committed
355
356
357
    result.append(QuickFixOperation::Ptr(
        new FlipLogicalOperandsOp(interface, index, binary, replacement)));
}
358

359
360
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
361
class RewriteLogicalAndOp: public CppQuickFixOperation
362
363
{
public:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
364
365
366
367
368
369
370
371
    QSharedPointer<ASTPatternBuilder> mk;
    UnaryExpressionAST *left;
    UnaryExpressionAST *right;
    BinaryExpressionAST *pattern;

    RewriteLogicalAndOp(const CppQuickFixInterface &interface)
        : CppQuickFixOperation(interface)
        , mk(new ASTPatternBuilder)
372
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
373
374
375
376
        left = mk->UnaryExpression();
        right = mk->UnaryExpression();
        pattern = mk->BinaryExpression(left, right);
    }
377

Nikolai Kosjar's avatar
Nikolai Kosjar committed
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
    void perform()
    {
        CppRefactoringChanges refactoring(snapshot());
        CppRefactoringFilePtr currentFile = refactoring.file(fileName());

        ChangeSet changes;
        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);
        changes.insert(start, QLatin1String("!("));
        changes.insert(end, QLatin1String(")"));

        currentFile->setChangeSet(changes);
        currentFile->appendIndentRange(currentFile->range(pattern));
        currentFile->apply();
    }
};
397

398
399
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
400
401
402
403
404
void RewriteLogicalAnd::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
{
    BinaryExpressionAST *expression = 0;
    const QList<AST *> &path = interface->path();
    CppRefactoringFilePtr file = interface->currentFile();
405

Nikolai Kosjar's avatar
Nikolai Kosjar committed
406
407
408
409
410
    int index = path.size() - 1;
    for (; index != -1; --index) {
        expression = path.at(index)->asBinaryExpression();
        if (expression)
            break;
411
412
    }

Nikolai Kosjar's avatar
Nikolai Kosjar committed
413
414
    if (! expression)
        return;
415

Nikolai Kosjar's avatar
Nikolai Kosjar committed
416
417
    if (! interface->isCursorOn(expression->binary_op_token))
        return;
418

Nikolai Kosjar's avatar
Nikolai Kosjar committed
419
    QSharedPointer<RewriteLogicalAndOp> op(new RewriteLogicalAndOp(interface));
420

Nikolai Kosjar's avatar
Nikolai Kosjar committed
421
422
423
424
    if (expression->match(op->pattern, &matcher) &&
            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)) {
425
426
        op->setDescription(QApplication::translate("CppTools::QuickFix",
                                                   "Rewrite Condition Using ||"));
Nikolai Kosjar's avatar
Nikolai Kosjar committed
427
428
429
430
        op->setPriority(index);
        result.append(op);
    }
}
431

Nikolai Kosjar's avatar
Nikolai Kosjar committed
432
433
434
435
bool SplitSimpleDeclaration::checkDeclaration(SimpleDeclarationAST *declaration)
{
    if (! declaration->semicolon_token)
        return false;
436

Nikolai Kosjar's avatar
Nikolai Kosjar committed
437
438
    if (! declaration->decl_specifier_list)
        return false;
439

Nikolai Kosjar's avatar
Nikolai Kosjar committed
440
441
    for (SpecifierListAST *it = declaration->decl_specifier_list; it; it = it->next) {
        SpecifierAST *specifier = it->value;
442

Nikolai Kosjar's avatar
Nikolai Kosjar committed
443
        if (specifier->asEnumSpecifier() != 0)
444
445
            return false;

Nikolai Kosjar's avatar
Nikolai Kosjar committed
446
        else if (specifier->asClassSpecifier() != 0)
447
            return false;
Nikolai Kosjar's avatar
Nikolai Kosjar committed
448
    }
449

Nikolai Kosjar's avatar
Nikolai Kosjar committed
450
451
    if (! declaration->declarator_list)
        return false;
452

Nikolai Kosjar's avatar
Nikolai Kosjar committed
453
454
    else if (! declaration->declarator_list->next)
        return false;
455

Nikolai Kosjar's avatar
Nikolai Kosjar committed
456
457
    return true;
}
458

459
460
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
461
462
463
464
465
466
467
468
469
470
class SplitSimpleDeclarationOp: public CppQuickFixOperation
{
public:
    SplitSimpleDeclarationOp(const CppQuickFixInterface &interface, int priority,
                             SimpleDeclarationAST *decl)
        : CppQuickFixOperation(interface, priority)
        , declaration(decl)
    {
        setDescription(QApplication::translate("CppTools::QuickFix",
                                               "Split Declaration"));
471
472
    }

Nikolai Kosjar's avatar
Nikolai Kosjar committed
473
    void perform()
474
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
475
476
        CppRefactoringChanges refactoring(snapshot());
        CppRefactoringFilePtr currentFile = refactoring.file(fileName());
477

Nikolai Kosjar's avatar
Nikolai Kosjar committed
478
        ChangeSet changes;
479

Nikolai Kosjar's avatar
Nikolai Kosjar committed
480
481
482
483
        SpecifierListAST *specifiers = declaration->decl_specifier_list;
        int declSpecifiersStart = currentFile->startOf(specifiers->firstToken());
        int declSpecifiersEnd = currentFile->endOf(specifiers->lastToken() - 1);
        int insertPos = currentFile->endOf(declaration->semicolon_token);
484

Nikolai Kosjar's avatar
Nikolai Kosjar committed
485
        DeclaratorAST *prevDeclarator = declaration->declarator_list->value;
486

Nikolai Kosjar's avatar
Nikolai Kosjar committed
487
488
        for (DeclaratorListAST *it = declaration->declarator_list->next; it; it = it->next) {
            DeclaratorAST *declarator = it->value;
489

Nikolai Kosjar's avatar
Nikolai Kosjar committed
490
491
492
493
494
            changes.insert(insertPos, QLatin1String("\n"));
            changes.copy(declSpecifiersStart, declSpecifiersEnd, insertPos);
            changes.insert(insertPos, QLatin1String(" "));
            changes.move(currentFile->range(declarator), insertPos);
            changes.insert(insertPos, QLatin1String(";"));
495

Nikolai Kosjar's avatar
Nikolai Kosjar committed
496
497
            const int prevDeclEnd = currentFile->endOf(prevDeclarator);
            changes.remove(prevDeclEnd, currentFile->startOf(declarator));
498

Nikolai Kosjar's avatar
Nikolai Kosjar committed
499
            prevDeclarator = declarator;
500
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
501
502
503
504

        currentFile->setChangeSet(changes);
        currentFile->appendIndentRange(currentFile->range(declaration));
        currentFile->apply();
505
506
507
    }

private:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
508
509
    SimpleDeclarationAST *declaration;
};
510

511
512
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
513
514
515
516
517
518
519
void SplitSimpleDeclaration::match(const CppQuickFixInterface &interface,
                                   QuickFixOperations &result)
{
    CoreDeclaratorAST *core_declarator = 0;
    const QList<AST *> &path = interface->path();
    CppRefactoringFilePtr file = interface->currentFile();
    const int cursorPosition = file->cursor().selectionStart();
520

Nikolai Kosjar's avatar
Nikolai Kosjar committed
521
522
    for (int index = path.size() - 1; index != -1; --index) {
        AST *node = path.at(index);
523

Nikolai Kosjar's avatar
Nikolai Kosjar committed
524
525
        if (CoreDeclaratorAST *coreDecl = node->asCoreDeclarator())
            core_declarator = coreDecl;
526

Nikolai Kosjar's avatar
Nikolai Kosjar committed
527
528
529
        else if (SimpleDeclarationAST *simpleDecl = node->asSimpleDeclaration()) {
            if (checkDeclaration(simpleDecl)) {
                SimpleDeclarationAST *declaration = simpleDecl;
530

Nikolai Kosjar's avatar
Nikolai Kosjar committed
531
532
                const int startOfDeclSpecifier = file->startOf(declaration->decl_specifier_list->firstToken());
                const int endOfDeclSpecifier = file->endOf(declaration->decl_specifier_list->lastToken() - 1);
533

Nikolai Kosjar's avatar
Nikolai Kosjar committed
534
535
536
537
538
539
                if (cursorPosition >= startOfDeclSpecifier && cursorPosition <= endOfDeclSpecifier) {
                    // the AST node under cursor is a specifier.
                    result.append(QuickFixOperation::Ptr(
                        new SplitSimpleDeclarationOp(interface, index, declaration)));
                    return;
                }
540

Nikolai Kosjar's avatar
Nikolai Kosjar committed
541
542
543
544
545
546
                if (core_declarator && interface->isCursorOn(core_declarator)) {
                    // got a core-declarator under the text cursor.
                    result.append(QuickFixOperation::Ptr(
                        new SplitSimpleDeclarationOp(interface, index, declaration)));
                    return;
                }
547
548
            }

Nikolai Kosjar's avatar
Nikolai Kosjar committed
549
            return;
550
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
551
552
    }
}
553

554
555
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
556
557
558
559
560
561
562
class AddBracesToIfOp: public CppQuickFixOperation
{
public:
    AddBracesToIfOp(const CppQuickFixInterface &interface, int priority, StatementAST *statement)
        : CppQuickFixOperation(interface, priority)
        , _statement(statement)
    {
563
        setDescription(QApplication::translate("CppTools::QuickFix", "Add Curly Braces"));
Nikolai Kosjar's avatar
Nikolai Kosjar committed
564
565
566
567
568
569
570
571
    }

    void perform()
    {
        CppRefactoringChanges refactoring(snapshot());
        CppRefactoringFilePtr currentFile = refactoring.file(fileName());

        ChangeSet changes;
572

Nikolai Kosjar's avatar
Nikolai Kosjar committed
573
574
        const int start = currentFile->endOf(_statement->firstToken() - 1);
        changes.insert(start, QLatin1String(" {"));
575

Nikolai Kosjar's avatar
Nikolai Kosjar committed
576
577
        const int end = currentFile->endOf(_statement->lastToken() - 1);
        changes.insert(end, QLatin1String("\n}"));
578

Nikolai Kosjar's avatar
Nikolai Kosjar committed
579
        currentFile->setChangeSet(changes);
580
        currentFile->appendIndentRange(ChangeSet::Range(start, end));
Nikolai Kosjar's avatar
Nikolai Kosjar committed
581
582
583
584
585
586
587
        currentFile->apply();
    }

private:
    StatementAST *_statement;
};

588
589
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
590
void AddBracesToIf::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
591
{
Nikolai Kosjar's avatar
Nikolai Kosjar committed
592
    const QList<AST *> &path = interface->path();
593

Nikolai Kosjar's avatar
Nikolai Kosjar committed
594
595
596
597
598
599
600
601
602
603
604
605
606
    // show when we're on the 'if' of an if statement
    int index = path.size() - 1;
    IfStatementAST *ifStatement = path.at(index)->asIfStatement();
    if (ifStatement && interface->isCursorOn(ifStatement->if_token) && ifStatement->statement
        && ! ifStatement->statement->asCompoundStatement()) {
        result.append(QuickFixOperation::Ptr(
            new AddBracesToIfOp(interface, index, ifStatement->statement)));
        return;
    }

    // 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) {
607
        IfStatementAST *ifStatement = path.at(index)->asIfStatement();
Nikolai Kosjar's avatar
Nikolai Kosjar committed
608
609
        if (ifStatement && ifStatement->statement
            && interface->isCursorOn(ifStatement->statement)
610
            && ! ifStatement->statement->asCompoundStatement()) {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
611
612
            result.append(QuickFixOperation::Ptr(
                new AddBracesToIfOp(interface, index, ifStatement->statement)));
613
            return;
614
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
615
    }
616

Nikolai Kosjar's avatar
Nikolai Kosjar committed
617
618
619
    // ### This could very well be extended to the else branch
    // and other nodes entirely.
}
620

621
622
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
623
624
625
626
627
628
629
630
631
632
633
class MoveDeclarationOutOfIfOp: public CppQuickFixOperation
{
public:
    MoveDeclarationOutOfIfOp(const CppQuickFixInterface &interface)
        : CppQuickFixOperation(interface)
    {
        setDescription(QApplication::translate("CppTools::QuickFix",
                                               "Move Declaration out of Condition"));

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
636
    void perform()
637
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
638
639
        CppRefactoringChanges refactoring(snapshot());
        CppRefactoringFilePtr currentFile = refactoring.file(fileName());
640

Nikolai Kosjar's avatar
Nikolai Kosjar committed
641
        ChangeSet changes;
642

Nikolai Kosjar's avatar
Nikolai Kosjar committed
643
        changes.copy(currentFile->range(core), currentFile->startOf(condition));
644

Nikolai Kosjar's avatar
Nikolai Kosjar committed
645
646
647
        int insertPos = currentFile->startOf(pattern);
        changes.move(currentFile->range(condition), insertPos);
        changes.insert(insertPos, QLatin1String(";\n"));
648

Nikolai Kosjar's avatar
Nikolai Kosjar committed
649
650
651
652
        currentFile->setChangeSet(changes);
        currentFile->appendIndentRange(currentFile->range(pattern));
        currentFile->apply();
    }
653

Nikolai Kosjar's avatar
Nikolai Kosjar committed
654
655
656
657
658
659
    ASTMatcher matcher;
    ASTPatternBuilder mk;
    CPPEditorWidget *editor;
    ConditionAST *condition;
    IfStatementAST *pattern;
    CoreDeclaratorAST *core;
660
661
};

662
663
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
664
665
void MoveDeclarationOutOfIf::match(const CppQuickFixInterface &interface,
                                   QuickFixOperations &result)
666
{
Nikolai Kosjar's avatar
Nikolai Kosjar committed
667
668
669
670
671
672
673
674
675
676
677
678
    const QList<AST *> &path = interface->path();
    typedef QSharedPointer<MoveDeclarationOutOfIfOp> Ptr;
    Ptr op(new MoveDeclarationOutOfIfOp(interface));

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
680
681
682
683
                if (interface->isCursorOn(op->core)) {
                    op->setPriority(index);
                    result.append(op);
                    return;
684
685
686
687
                }
            }
        }
    }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
688
}
689

690
691
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
692
693
694
695
696
class MoveDeclarationOutOfWhileOp: public CppQuickFixOperation
{
public:
    MoveDeclarationOutOfWhileOp(const CppQuickFixInterface &interface)
        : CppQuickFixOperation(interface)
697
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
698
699
        setDescription(QApplication::translate("CppTools::QuickFix",
                                               "Move Declaration out of Condition"));
700

Nikolai Kosjar's avatar
Nikolai Kosjar committed
701
702
703
        condition = mk.Condition();
        pattern = mk.WhileStatement(condition);
    }
704

Nikolai Kosjar's avatar
Nikolai Kosjar committed
705
706
707
708
    void perform()
    {
        CppRefactoringChanges refactoring(snapshot());
        CppRefactoringFilePtr currentFile = refactoring.file(fileName());
709

Nikolai Kosjar's avatar
Nikolai Kosjar committed
710
        ChangeSet changes;
711

Nikolai Kosjar's avatar
Nikolai Kosjar committed
712
713
        changes.insert(currentFile->startOf(condition), QLatin1String("("));
        changes.insert(currentFile->endOf(condition), QLatin1String(") != 0"));
714

Nikolai Kosjar's avatar
Nikolai Kosjar committed
715
716
717
718
719
        int insertPos = currentFile->startOf(pattern);
        const int conditionStart = currentFile->startOf(condition);
        changes.move(conditionStart, currentFile->startOf(core), insertPos);
        changes.copy(currentFile->range(core), insertPos);
        changes.insert(insertPos, QLatin1String(";\n"));
720

Nikolai Kosjar's avatar
Nikolai Kosjar committed
721
722
723
724
        currentFile->setChangeSet(changes);
        currentFile->appendIndentRange(currentFile->range(pattern));
        currentFile->apply();
    }
725

Nikolai Kosjar's avatar
Nikolai Kosjar committed
726
727
728
729
730
731
732
    ASTMatcher matcher;
    ASTPatternBuilder mk;
    CPPEditorWidget *editor;
    ConditionAST *condition;
    WhileStatementAST *pattern;
    CoreDeclaratorAST *core;
};
733

734
735
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
736
737
void MoveDeclarationOutOfWhile::match(const CppQuickFixInterface &interface,
                                      QuickFixOperations &result)
738
{
Nikolai Kosjar's avatar
Nikolai Kosjar committed
739
740
    const QList<AST *> &path = interface->path();
    QSharedPointer<MoveDeclarationOutOfWhileOp> op(new MoveDeclarationOutOfWhileOp(interface));
741

Nikolai Kosjar's avatar
Nikolai Kosjar committed
742
743
744
745
746
747
    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;
748

Nikolai Kosjar's avatar
Nikolai Kosjar committed
749
750
                if (! op->core)
                    return;
751

Nikolai Kosjar's avatar
Nikolai Kosjar committed
752
753
                if (! declarator->equal_token)
                    return;
754

Nikolai Kosjar's avatar
Nikolai Kosjar committed
755
756
                if (! declarator->initializer)
                    return;
757

Nikolai Kosjar's avatar
Nikolai Kosjar committed
758
759
760
761
                if (interface->isCursorOn(op->core)) {
                    op->setPriority(index);
                    result.append(op);
                    return;
762
763
764
765
                }
            }
        }
    }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
766
}
767

768
769
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
770
771
772
773
774
775
776
777
class SplitIfStatementOp: public CppQuickFixOperation
{
public:
    SplitIfStatementOp(const CppQuickFixInterface &interface, int priority,
                       IfStatementAST *pattern, BinaryExpressionAST *condition)
        : CppQuickFixOperation(interface, priority)
        , pattern(pattern)
        , condition(condition)
778
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
779
780
781
782
783
784
785
786
        setDescription(QApplication::translate("CppTools::QuickFix",
                                               "Split if Statement"));
    }

    void perform()
    {
        CppRefactoringChanges refactoring(snapshot());
        CppRefactoringFilePtr currentFile = refactoring.file(fileName());
787

Nikolai Kosjar's avatar
Nikolai Kosjar committed
788
        const Token binaryToken = currentFile->tokenAt(condition->binary_op_token);
789

Nikolai Kosjar's avatar
Nikolai Kosjar committed
790
791
792
793
794
        if (binaryToken.is(T_AMPER_AMPER))
            splitAndCondition(currentFile);
        else
            splitOrCondition(currentFile);
    }
795

Nikolai Kosjar's avatar
Nikolai Kosjar committed
796
797
798
    void splitAndCondition(CppRefactoringFilePtr currentFile) const
    {
        ChangeSet changes;
799

Nikolai Kosjar's avatar
Nikolai Kosjar committed
800
801
802
803
        int startPos = currentFile->startOf(pattern);
        changes.insert(startPos, QLatin1String("if ("));
        changes.move(currentFile->range(condition->left_expression), startPos);
        changes.insert(startPos, QLatin1String(") {\n"));
804

Nikolai Kosjar's avatar
Nikolai Kosjar committed
805
806
807
        const int lExprEnd = currentFile->endOf(condition->left_expression);
        changes.remove(lExprEnd, currentFile->startOf(condition->right_expression));
        changes.insert(currentFile->endOf(pattern), QLatin1String("\n}"));
808

Nikolai Kosjar's avatar
Nikolai Kosjar committed
809
810
811
812
        currentFile->setChangeSet(changes);
        currentFile->appendIndentRange(currentFile->range(pattern));
        currentFile->apply();
    }
813

Nikolai Kosjar's avatar
Nikolai Kosjar committed
814
    void splitOrCondition(CppRefactoringFilePtr currentFile) const
815
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
816
        ChangeSet changes;
817

Nikolai Kosjar's avatar
Nikolai Kosjar committed
818
819
        StatementAST *ifTrueStatement = pattern->statement;
        CompoundStatementAST *compoundStatement = ifTrueStatement->asCompoundStatement();
820

Nikolai Kosjar's avatar
Nikolai Kosjar committed
821
822
823
824
825
826
        int insertPos = currentFile->endOf(ifTrueStatement);
        if (compoundStatement)
            changes.insert(insertPos, QLatin1String(" "));
        else
            changes.insert(insertPos, QLatin1String("\n"));
        changes.insert(insertPos, QLatin1String("else if ("));
827

Nikolai Kosjar's avatar
Nikolai Kosjar committed
828
829
830
        const int rExprStart = currentFile->startOf(condition->right_expression);
        changes.move(rExprStart, currentFile->startOf(pattern->rparen_token), insertPos);
        changes.insert(insertPos, QLatin1String(")"));
831

Nikolai Kosjar's avatar
Nikolai Kosjar committed
832
833
        const int rParenEnd = currentFile->endOf(pattern->rparen_token);
        changes.copy(rParenEnd, currentFile->endOf(pattern->statement), insertPos);
834

Nikolai Kosjar's avatar
Nikolai Kosjar committed
835
836
837
838
839
840
        const int lExprEnd = currentFile->endOf(condition->left_expression);
        changes.remove(lExprEnd, currentFile->startOf(condition->right_expression));

        currentFile->setChangeSet(changes);
        currentFile->appendIndentRange(currentFile->range(pattern));
        currentFile->apply();
841
842
843
    }

private:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
844
845
846
    IfStatementAST *pattern;
    BinaryExpressionAST *condition;
};
847

848
849
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
850
851
852
853
void SplitIfStatement::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
{
    IfStatementAST *pattern = 0;
    const QList<AST *> &path = interface->path();
854

Nikolai Kosjar's avatar
Nikolai Kosjar committed
855
856
857
858
859
860
    int index = path.size() - 1;
    for (; index != -1; --index) {
        AST *node = path.at(index);
        if (IfStatementAST *stmt = node->asIfStatement()) {
            pattern = stmt;
            break;
861
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
862
    }
863

Nikolai Kosjar's avatar
Nikolai Kosjar committed
864
865
    if (! pattern || ! pattern->statement)
        return;
866

Nikolai Kosjar's avatar
Nikolai Kosjar committed
867
868
869
870
871
872
    unsigned splitKind = 0;
    for (++index; index < path.size(); ++index) {
        AST *node = path.at(index);
        BinaryExpressionAST *condition = node->asBinaryExpression();
        if (! condition)
            return;
873

Nikolai Kosjar's avatar
Nikolai Kosjar committed
874
        Token binaryToken = interface->currentFile()->tokenAt(condition->binary_op_token);
875

Nikolai Kosjar's avatar
Nikolai Kosjar committed
876
877
878
879
880
881
882
883
884
885
        // 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;
            // we can't reliably split &&s in ifs with an else branch
            if (splitKind == T_AMPER_AMPER && pattern->else_statement)
                return;
        } else if (splitKind != binaryToken.kind()) {
            return;
886
887
        }

Nikolai Kosjar's avatar
Nikolai Kosjar committed
888
889
890
891
        if (interface->isCursorOn(condition->binary_op_token)) {
            result.append(QuickFixOperation::Ptr(
                new SplitIfStatementOp(interface, index, pattern, condition)));
            return;
892
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
893
894
    }
}
895

896
897
898
899
/* Analze a string/character literal like "x", QLatin1String("x") and return the literal
 * (StringLiteral or NumericLiteral for characters) and its type
 * and the enclosing function (QLatin1String, tr...) */
ExpressionAST *WrapStringLiteral::analyze(const QList<AST *> &path,
900
                                          const CppRefactoringFilePtr &file, Type *type,
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
                                          QByteArray *enclosingFunction /* = 0 */,
                                          CallAST **enclosingFunctionCall /* = 0 */)
{
    *type = TypeNone;
    if (enclosingFunction)
        enclosingFunction->clear();
    if (enclosingFunctionCall)
        *enclosingFunctionCall = 0;

    if (path.isEmpty())
        return 0;

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

926
927
928
929
930
931
932
933
    if (*type != TypeNone && enclosingFunction && path.size() > 1) {
        if (CallAST *call = path.at(path.size() - 2)->asCall()) {
            if (call->base_expression) {
                if (IdExpressionAST *idExpr = call->base_expression->asIdExpression()) {
                    if (SimpleNameAST *functionName = idExpr->name->asSimpleName()) {
                        *enclosingFunction = file->tokenAt(functionName->identifier_token).identifier->chars();
                        if (enclosingFunctionCall)
                            *enclosingFunctionCall = call;
934
935
936
937
                    }
                }
            }
        }
938
939
940
    }
    return literal;
}
941

942
943
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
/// Operation performs the operations of type ActionFlags passed in as actions.
class WrapStringLiteralOp : public CppQuickFixOperation
{
public:
    typedef WrapStringLiteral Factory;

    WrapStringLiteralOp(const CppQuickFixInterface &interface, int priority,
                        unsigned actions, const QString &description, ExpressionAST *literal,
                        const QString &translationContext = QString())
        : CppQuickFixOperation(interface, priority), m_actions(actions), m_literal(literal),
          m_translationContext(translationContext)
    {
        setDescription(description);
    }

    void perform()
    {
        CppRefactoringChanges refactoring(snapshot());
        CppRefactoringFilePtr currentFile = refactoring.file(fileName());

        ChangeSet changes;

        const int startPos = currentFile->startOf(m_literal);
        const int endPos = currentFile->endOf(m_literal);

        // kill leading '@'. No need to adapt endPos, that is done by ChangeSet
        if (m_actions & Factory::RemoveObjectiveCAction)
            changes.remove(startPos, startPos + 1);

        // Fix quotes
        if (m_actions & (Factory::SingleQuoteAction | Factory::DoubleQuoteAction)) {
975
976
            const QString newQuote((m_actions & Factory::SingleQuoteAction)
                                   ? QLatin1Char('\'') : QLatin1Char('"'));
Nikolai Kosjar's avatar
Nikolai Kosjar committed
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
            changes.replace(startPos, startPos + 1, newQuote);
            changes.replace(endPos - 1, endPos, newQuote);
        }

        // Convert single character strings into character constants
        if (m_actions & Factory::ConvertEscapeSequencesToCharAction) {
            StringLiteralAST *stringLiteral = m_literal->asStringLiteral();
            QTC_ASSERT(stringLiteral, return ;);
            const QByteArray oldContents(currentFile->tokenAt(stringLiteral->literal_token).identifier->chars());
            const QByteArray newContents = Factory::stringToCharEscapeSequences(oldContents);
            QTC_ASSERT(!newContents.isEmpty(), return ;);
            if (oldContents != newContents)
                changes.replace(startPos + 1, endPos -1, QString::fromLatin1(newContents));
        }

        // Convert character constants into strings constants
        if (m_actions & Factory::ConvertEscapeSequencesToStringAction) {
            NumericLiteralAST *charLiteral = m_literal->asNumericLiteral(); // char 'c' constants are numerical.
            QTC_ASSERT(charLiteral, return ;);
            const QByteArray oldContents(currentFile->tokenAt(charLiteral->literal_token).identifier->chars());
            const QByteArray newContents = Factory::charToStringEscapeSequences(oldContents);
            QTC_ASSERT(!newContents.isEmpty(), return ;);
            if (oldContents != newContents)
                changes.replace(startPos + 1, endPos -1, QString::fromLatin1(newContents));
        }

        // Enclose in literal or translation function, macro.
        if (m_actions & (Factory::EncloseActionMask | Factory::TranslationMask)) {
            changes.insert(endPos, QString(QLatin1Char(')')));
            QString leading = Factory::replacement(m_actions);
            leading += QLatin1Char('(');
1008
1009
            if (m_actions
                    & (Factory::TranslateQCoreApplicationAction | Factory::TranslateNoopAction)) {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
                leading += QLatin1Char('"');
                leading += m_translationContext;
                leading += QLatin1String("\", ");
            }
            changes.insert(startPos, leading);
        }

        currentFile->setChangeSet(changes);
        currentFile->apply();
    }

private:
    const unsigned m_actions;
    ExpressionAST *m_literal;
    const QString m_translationContext;
};

1027
1028
} // anonymous namespace

1029
void WrapStringLiteral::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
1030
1031
1032
1033
1034
1035
1036
{
    typedef CppQuickFixOperation::Ptr OperationPtr;

    Type type = TypeNone;
    QByteArray enclosingFunction;
    const QList<AST *> &path = interface->path();
    CppRefactoringFilePtr file = interface->currentFile();
Nikolai Kosjar's avatar
Nikolai Kosjar committed
1037
    ExpressionAST *literal = analyze(path, file, &type, &enclosingFunction);
1038
    if (!literal || type == TypeNone)
1039
        return;
1040
1041
1042
    if ((type == TypeChar && enclosingFunction == "QLatin1Char")
        || isQtStringLiteral(enclosingFunction)
        || isQtStringTranslation(enclosingFunction))
1043
        return;
1044

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

1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
QString WrapStringLiteral::replacement(unsigned actions)
{
    if (actions & EncloseInQLatin1CharAction)
        return QLatin1String("QLatin1Char");
    if (actions & EncloseInQLatin1StringAction)
        return QLatin1String("QLatin1String");
    if (actions & EncloseInQStringLiteralAction)
        return QLatin1String("QStringLiteral");
    if (actions & TranslateTrAction)
        return QLatin1String("tr");
    if (actions & TranslateQCoreApplicationAction)
        return QLatin1String("QCoreApplication::translate");
    if (actions & TranslateNoopAction)
        return QLatin1String("QT_TRANSLATE_NOOP");
    return QString();
}
1109

1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
/* Convert single-character string literals into character literals with some
 * special cases "a" --> 'a', "'" --> '\'', "\n" --> '\n', "\"" --> '"'. */
QByteArray WrapStringLiteral::stringToCharEscapeSequences(const QByteArray &content)
{
    if (content.size() == 1)
        return content.at(0) == '\'' ? QByteArray("\\'") : content;
    if (content.size() == 2 && content.at(0) == '\\')
        return content == "\\\"" ? QByteArray(1, '"') : content;
    return QByteArray();
}
1120

1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
/* Convert character literal into a string literal with some special cases
 * 'a' -> "a", '\n' -> "\n", '\'' --> "'", '"' --> "\"". */
QByteArray WrapStringLiteral::charToStringEscapeSequences(const QByteArray &content)
{
    if (content.size() == 1)
        return content.at(0) == '"' ? QByteArray("\\\"") : content;
    if (content.size() == 2)
        return content == "\\'" ? QByteArray("'") : content;
    return QByteArray();
}
1131

1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
inline QString WrapStringLiteral::msgQtStringLiteralDescription(const QString &replacement,
                                                                       int qtVersion)
{
    return QApplication::translate("CppTools::QuickFix", "Enclose in %1(...) (Qt %2)")
           .arg(replacement).arg(qtVersion);
}

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
1144
1145
void TranslateStringLiteral::match(const CppQuickFixInterface &interface,
                                   QuickFixOperations &result)
1146
{
Nikolai Kosjar's avatar
Nikolai Kosjar committed
1147
1148
1149
1150
1151
1152
1153
1154
1155
    // Initialize
    WrapStringLiteral::Type type = WrapStringLiteral::TypeNone;
    QByteArray enclosingFunction;
    const QList<AST *> &path = interface->path();