cppquickfixes.cpp 146 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);
Nikolai Kosjar's avatar
Nikolai Kosjar committed
110
}
111

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

inline bool isQtStringLiteral(const QByteArray &id)
117
118
119
120
{
    return id == "QLatin1String" || id == "QLatin1Literal" || id == "QStringLiteral";
}

121
inline bool isQtStringTranslation(const QByteArray &id)
122
123
124
125
{
    return id == "tr" || id == "trUtf8" || id == "translate" || id == "QT_TRANSLATE_NOOP";
}

126
127
128
129
130
131
132
133
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
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
160
class InverseLogicalComparisonOp: public CppQuickFixOperation
161
162
{
public:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
163
164
165
166
    InverseLogicalComparisonOp(const CppQuickFixInterface &interface, int priority,
                               BinaryExpressionAST *binary, Kind invertToken)
        : CppQuickFixOperation(interface, priority)
        , binary(binary), nested(0), negation(0)
167
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
168
169
170
        Token tok;
        tok.f.kind = invertToken;
        replacement = QLatin1String(tok.spell());
171

Nikolai Kosjar's avatar
Nikolai Kosjar committed
172
173
174
        // check for enclosing nested expression
        if (priority - 1 >= 0)
            nested = interface->path()[priority - 1]->asNestedExpression();
175

Nikolai Kosjar's avatar
Nikolai Kosjar committed
176
177
178
179
180
        // 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;
181
182
183
        }
    }

Nikolai Kosjar's avatar
Nikolai Kosjar committed
184
    QString description() const
185
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
186
187
        return QApplication::translate("CppTools::QuickFix", "Rewrite Using %1").arg(replacement);
    }
188

Nikolai Kosjar's avatar
Nikolai Kosjar committed
189
190
191
192
193
194
195
196
197
198
199
200
201
202
    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(")"));
203
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
204
205
206
207
        changes.replace(currentFile->range(binary->binary_op_token), replacement);
        currentFile->setChangeSet(changes);
        currentFile->apply();
    }
208

Nikolai Kosjar's avatar
Nikolai Kosjar committed
209
210
211
212
private:
    BinaryExpressionAST *binary;
    NestedExpressionAST *nested;
    UnaryExpressionAST *negation;
213

Nikolai Kosjar's avatar
Nikolai Kosjar committed
214
    QString replacement;
215
216
};

217
218
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
219
220
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
250
251
252
253
254
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;
    }
255

Nikolai Kosjar's avatar
Nikolai Kosjar committed
256
257
258
    result.append(CppQuickFixOperation::Ptr(
        new InverseLogicalComparisonOp(interface, index, binary, invertToken)));
}
259

260
261
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
262
class FlipLogicalOperandsOp: public CppQuickFixOperation
263
264
{
public:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
265
266
267
268
269
    FlipLogicalOperandsOp(const CppQuickFixInterface &interface, int priority,
                          BinaryExpressionAST *binary, QString replacement)
        : CppQuickFixOperation(interface)
        , binary(binary)
        , replacement(replacement)
270
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
271
272
        setPriority(priority);
    }
273

Nikolai Kosjar's avatar
Nikolai Kosjar committed
274
275
276
277
278
279
280
    QString description() const
    {
        if (replacement.isEmpty())
            return QApplication::translate("CppTools::QuickFix", "Swap Operands");
        else
            return QApplication::translate("CppTools::QuickFix", "Rewrite Using %1").arg(replacement);
    }
281

Nikolai Kosjar's avatar
Nikolai Kosjar committed
282
283
284
285
    void perform()
    {
        CppRefactoringChanges refactoring(snapshot());
        CppRefactoringFilePtr currentFile = refactoring.file(fileName());
286

Nikolai Kosjar's avatar
Nikolai Kosjar committed
287
        ChangeSet changes;
288
289
        changes.flip(currentFile->range(binary->left_expression),
                     currentFile->range(binary->right_expression));
Nikolai Kosjar's avatar
Nikolai Kosjar committed
290
291
        if (! replacement.isEmpty())
            changes.replace(currentFile->range(binary->binary_op_token), replacement);
292

Nikolai Kosjar's avatar
Nikolai Kosjar committed
293
294
        currentFile->setChangeSet(changes);
        currentFile->apply();
295
296
297
    }

private:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
298
299
300
    BinaryExpressionAST *binary;
    QString replacement;
};
301

302
303
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
304
305
306
307
void FlipLogicalOperands::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
{
    const QList<AST *> &path = interface->path();
    CppRefactoringFilePtr file = interface->currentFile();
308

Nikolai Kosjar's avatar
Nikolai Kosjar committed
309
310
311
312
313
314
    int index = path.size() - 1;
    BinaryExpressionAST *binary = path.at(index)->asBinaryExpression();
    if (! binary)
        return;
    if (! interface->isCursorOn(binary->binary_op_token))
        return;
315

Nikolai Kosjar's avatar
Nikolai Kosjar committed
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
    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;
    }
339

Nikolai Kosjar's avatar
Nikolai Kosjar committed
340
341
342
343
344
345
    QString replacement;
    if (flipToken != T_EOF_SYMBOL) {
        Token tok;
        tok.f.kind = flipToken;
        replacement = QLatin1String(tok.spell());
    }
346

Nikolai Kosjar's avatar
Nikolai Kosjar committed
347
348
349
    result.append(QuickFixOperation::Ptr(
        new FlipLogicalOperandsOp(interface, index, binary, replacement)));
}
350

351
352
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
353
class RewriteLogicalAndOp: public CppQuickFixOperation
354
355
{
public:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
356
357
358
359
360
361
362
363
    QSharedPointer<ASTPatternBuilder> mk;
    UnaryExpressionAST *left;
    UnaryExpressionAST *right;
    BinaryExpressionAST *pattern;

    RewriteLogicalAndOp(const CppQuickFixInterface &interface)
        : CppQuickFixOperation(interface)
        , mk(new ASTPatternBuilder)
364
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
365
366
367
368
        left = mk->UnaryExpression();
        right = mk->UnaryExpression();
        pattern = mk->BinaryExpression(left, right);
    }
369

Nikolai Kosjar's avatar
Nikolai Kosjar committed
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
    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();
    }
};
389

390
391
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
392
393
394
395
396
void RewriteLogicalAnd::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
{
    BinaryExpressionAST *expression = 0;
    const QList<AST *> &path = interface->path();
    CppRefactoringFilePtr file = interface->currentFile();
397

Nikolai Kosjar's avatar
Nikolai Kosjar committed
398
399
400
401
402
    int index = path.size() - 1;
    for (; index != -1; --index) {
        expression = path.at(index)->asBinaryExpression();
        if (expression)
            break;
403
404
    }

Nikolai Kosjar's avatar
Nikolai Kosjar committed
405
406
    if (! expression)
        return;
407

Nikolai Kosjar's avatar
Nikolai Kosjar committed
408
409
    if (! interface->isCursorOn(expression->binary_op_token))
        return;
410

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
413
414
415
416
    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)) {
417
418
        op->setDescription(QApplication::translate("CppTools::QuickFix",
                                                   "Rewrite Condition Using ||"));
Nikolai Kosjar's avatar
Nikolai Kosjar committed
419
420
421
422
        op->setPriority(index);
        result.append(op);
    }
}
423

Nikolai Kosjar's avatar
Nikolai Kosjar committed
424
425
426
427
bool SplitSimpleDeclaration::checkDeclaration(SimpleDeclarationAST *declaration)
{
    if (! declaration->semicolon_token)
        return false;
428

Nikolai Kosjar's avatar
Nikolai Kosjar committed
429
430
    if (! declaration->decl_specifier_list)
        return false;
431

Nikolai Kosjar's avatar
Nikolai Kosjar committed
432
433
    for (SpecifierListAST *it = declaration->decl_specifier_list; it; it = it->next) {
        SpecifierAST *specifier = it->value;
434

Nikolai Kosjar's avatar
Nikolai Kosjar committed
435
        if (specifier->asEnumSpecifier() != 0)
436
437
            return false;

Nikolai Kosjar's avatar
Nikolai Kosjar committed
438
        else if (specifier->asClassSpecifier() != 0)
439
            return false;
Nikolai Kosjar's avatar
Nikolai Kosjar committed
440
    }
441

Nikolai Kosjar's avatar
Nikolai Kosjar committed
442
443
    if (! declaration->declarator_list)
        return false;
444

Nikolai Kosjar's avatar
Nikolai Kosjar committed
445
446
    else if (! declaration->declarator_list->next)
        return false;
447

Nikolai Kosjar's avatar
Nikolai Kosjar committed
448
449
    return true;
}
450

451
452
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
453
454
455
456
457
458
459
460
461
462
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"));
463
464
    }

Nikolai Kosjar's avatar
Nikolai Kosjar committed
465
    void perform()
466
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
467
468
        CppRefactoringChanges refactoring(snapshot());
        CppRefactoringFilePtr currentFile = refactoring.file(fileName());
469

Nikolai Kosjar's avatar
Nikolai Kosjar committed
470
        ChangeSet changes;
471

Nikolai Kosjar's avatar
Nikolai Kosjar committed
472
473
474
475
        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);
476

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
479
480
        for (DeclaratorListAST *it = declaration->declarator_list->next; it; it = it->next) {
            DeclaratorAST *declarator = it->value;
481

Nikolai Kosjar's avatar
Nikolai Kosjar committed
482
483
484
485
486
            changes.insert(insertPos, QLatin1String("\n"));
            changes.copy(declSpecifiersStart, declSpecifiersEnd, insertPos);
            changes.insert(insertPos, QLatin1String(" "));
            changes.move(currentFile->range(declarator), insertPos);
            changes.insert(insertPos, QLatin1String(";"));
487

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
491
            prevDeclarator = declarator;
492
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
493
494
495
496

        currentFile->setChangeSet(changes);
        currentFile->appendIndentRange(currentFile->range(declaration));
        currentFile->apply();
497
498
499
    }

private:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
500
501
    SimpleDeclarationAST *declaration;
};
502

503
504
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
505
506
507
508
509
510
511
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();
512

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
516
517
        if (CoreDeclaratorAST *coreDecl = node->asCoreDeclarator())
            core_declarator = coreDecl;
518

Nikolai Kosjar's avatar
Nikolai Kosjar committed
519
520
521
        else if (SimpleDeclarationAST *simpleDecl = node->asSimpleDeclaration()) {
            if (checkDeclaration(simpleDecl)) {
                SimpleDeclarationAST *declaration = simpleDecl;
522

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
526
527
528
529
530
531
                if (cursorPosition >= startOfDeclSpecifier && cursorPosition <= endOfDeclSpecifier) {
                    // the AST node under cursor is a specifier.
                    result.append(QuickFixOperation::Ptr(
                        new SplitSimpleDeclarationOp(interface, index, declaration)));
                    return;
                }
532

Nikolai Kosjar's avatar
Nikolai Kosjar committed
533
534
535
536
537
538
                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;
                }
539
540
            }

Nikolai Kosjar's avatar
Nikolai Kosjar committed
541
            return;
542
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
543
544
    }
}
545

546
547
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
548
549
550
551
552
553
554
class AddBracesToIfOp: public CppQuickFixOperation
{
public:
    AddBracesToIfOp(const CppQuickFixInterface &interface, int priority, StatementAST *statement)
        : CppQuickFixOperation(interface, priority)
        , _statement(statement)
    {
555
        setDescription(QApplication::translate("CppTools::QuickFix", "Add Curly Braces"));
Nikolai Kosjar's avatar
Nikolai Kosjar committed
556
557
558
559
560
561
562
563
    }

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

        ChangeSet changes;
564

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

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
571
        currentFile->setChangeSet(changes);
572
        currentFile->appendIndentRange(ChangeSet::Range(start, end));
Nikolai Kosjar's avatar
Nikolai Kosjar committed
573
574
575
576
577
578
579
        currentFile->apply();
    }

private:
    StatementAST *_statement;
};

580
581
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
582
void AddBracesToIf::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
583
{
Nikolai Kosjar's avatar
Nikolai Kosjar committed
584
    const QList<AST *> &path = interface->path();
585

Nikolai Kosjar's avatar
Nikolai Kosjar committed
586
587
588
589
590
591
592
593
594
595
596
597
598
    // 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) {
599
        IfStatementAST *ifStatement = path.at(index)->asIfStatement();
Nikolai Kosjar's avatar
Nikolai Kosjar committed
600
601
        if (ifStatement && ifStatement->statement
            && interface->isCursorOn(ifStatement->statement)
602
            && ! ifStatement->statement->asCompoundStatement()) {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
603
604
            result.append(QuickFixOperation::Ptr(
                new AddBracesToIfOp(interface, index, ifStatement->statement)));
605
            return;
606
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
607
    }
608

Nikolai Kosjar's avatar
Nikolai Kosjar committed
609
610
611
    // ### This could very well be extended to the else branch
    // and other nodes entirely.
}
612

613
614
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
615
616
617
618
619
620
621
622
623
624
625
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);
626
627
    }

Nikolai Kosjar's avatar
Nikolai Kosjar committed
628
    void perform()
629
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
630
631
        CppRefactoringChanges refactoring(snapshot());
        CppRefactoringFilePtr currentFile = refactoring.file(fileName());
632

Nikolai Kosjar's avatar
Nikolai Kosjar committed
633
        ChangeSet changes;
634

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
637
638
639
        int insertPos = currentFile->startOf(pattern);
        changes.move(currentFile->range(condition), insertPos);
        changes.insert(insertPos, QLatin1String(";\n"));
640

Nikolai Kosjar's avatar
Nikolai Kosjar committed
641
642
643
644
        currentFile->setChangeSet(changes);
        currentFile->appendIndentRange(currentFile->range(pattern));
        currentFile->apply();
    }
645

Nikolai Kosjar's avatar
Nikolai Kosjar committed
646
647
648
649
650
651
    ASTMatcher matcher;
    ASTPatternBuilder mk;
    CPPEditorWidget *editor;
    ConditionAST *condition;
    IfStatementAST *pattern;
    CoreDeclaratorAST *core;
652
653
};

654
655
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
656
657
void MoveDeclarationOutOfIf::match(const CppQuickFixInterface &interface,
                                   QuickFixOperations &result)
658
{
Nikolai Kosjar's avatar
Nikolai Kosjar committed
659
660
661
662
663
664
665
666
667
668
669
670
    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;
671

Nikolai Kosjar's avatar
Nikolai Kosjar committed
672
673
674
675
                if (interface->isCursorOn(op->core)) {
                    op->setPriority(index);
                    result.append(op);
                    return;
676
677
678
679
                }
            }
        }
    }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
680
}
681

682
683
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
684
685
686
687
688
class MoveDeclarationOutOfWhileOp: public CppQuickFixOperation
{
public:
    MoveDeclarationOutOfWhileOp(const CppQuickFixInterface &interface)
        : CppQuickFixOperation(interface)
689
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
690
691
        setDescription(QApplication::translate("CppTools::QuickFix",
                                               "Move Declaration out of Condition"));
692

Nikolai Kosjar's avatar
Nikolai Kosjar committed
693
694
695
        condition = mk.Condition();
        pattern = mk.WhileStatement(condition);
    }
696

Nikolai Kosjar's avatar
Nikolai Kosjar committed
697
698
699
700
    void perform()
    {
        CppRefactoringChanges refactoring(snapshot());
        CppRefactoringFilePtr currentFile = refactoring.file(fileName());
701

Nikolai Kosjar's avatar
Nikolai Kosjar committed
702
        ChangeSet changes;
703

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
707
708
709
710
711
        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"));
712

Nikolai Kosjar's avatar
Nikolai Kosjar committed
713
714
715
716
        currentFile->setChangeSet(changes);
        currentFile->appendIndentRange(currentFile->range(pattern));
        currentFile->apply();
    }
717

Nikolai Kosjar's avatar
Nikolai Kosjar committed
718
719
720
721
722
723
724
    ASTMatcher matcher;
    ASTPatternBuilder mk;
    CPPEditorWidget *editor;
    ConditionAST *condition;
    WhileStatementAST *pattern;
    CoreDeclaratorAST *core;
};
725

726
727
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
728
729
void MoveDeclarationOutOfWhile::match(const CppQuickFixInterface &interface,
                                      QuickFixOperations &result)
730
{
Nikolai Kosjar's avatar
Nikolai Kosjar committed
731
732
    const QList<AST *> &path = interface->path();
    QSharedPointer<MoveDeclarationOutOfWhileOp> op(new MoveDeclarationOutOfWhileOp(interface));
733

Nikolai Kosjar's avatar
Nikolai Kosjar committed
734
735
736
737
738
739
    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;
740

Nikolai Kosjar's avatar
Nikolai Kosjar committed
741
742
                if (! op->core)
                    return;
743

Nikolai Kosjar's avatar
Nikolai Kosjar committed
744
745
                if (! declarator->equal_token)
                    return;
746

Nikolai Kosjar's avatar
Nikolai Kosjar committed
747
748
                if (! declarator->initializer)
                    return;
749

Nikolai Kosjar's avatar
Nikolai Kosjar committed
750
751
752
753
                if (interface->isCursorOn(op->core)) {
                    op->setPriority(index);
                    result.append(op);
                    return;
754
755
756
757
                }
            }
        }
    }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
758
}
759

760
761
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
762
763
764
765
766
767
768
769
class SplitIfStatementOp: public CppQuickFixOperation
{
public:
    SplitIfStatementOp(const CppQuickFixInterface &interface, int priority,
                       IfStatementAST *pattern, BinaryExpressionAST *condition)
        : CppQuickFixOperation(interface, priority)
        , pattern(pattern)
        , condition(condition)
770
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
771
772
773
774
775
776
777
778
        setDescription(QApplication::translate("CppTools::QuickFix",
                                               "Split if Statement"));
    }

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

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
782
783
784
785
786
        if (binaryToken.is(T_AMPER_AMPER))
            splitAndCondition(currentFile);
        else
            splitOrCondition(currentFile);
    }
787

Nikolai Kosjar's avatar
Nikolai Kosjar committed
788
789
790
    void splitAndCondition(CppRefactoringFilePtr currentFile) const
    {
        ChangeSet changes;
791

Nikolai Kosjar's avatar
Nikolai Kosjar committed
792
793
794
795
        int startPos = currentFile->startOf(pattern);
        changes.insert(startPos, QLatin1String("if ("));
        changes.move(currentFile->range(condition->left_expression), startPos);
        changes.insert(startPos, QLatin1String(") {\n"));
796

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
801
802
803
804
        currentFile->setChangeSet(changes);
        currentFile->appendIndentRange(currentFile->range(pattern));
        currentFile->apply();
    }
805

Nikolai Kosjar's avatar
Nikolai Kosjar committed
806
    void splitOrCondition(CppRefactoringFilePtr currentFile) const
807
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
808
        ChangeSet changes;
809

Nikolai Kosjar's avatar
Nikolai Kosjar committed
810
811
        StatementAST *ifTrueStatement = pattern->statement;
        CompoundStatementAST *compoundStatement = ifTrueStatement->asCompoundStatement();
812

Nikolai Kosjar's avatar
Nikolai Kosjar committed
813
814
815
816
817
818
        int insertPos = currentFile->endOf(ifTrueStatement);
        if (compoundStatement)
            changes.insert(insertPos, QLatin1String(" "));
        else
            changes.insert(insertPos, QLatin1String("\n"));
        changes.insert(insertPos, QLatin1String("else if ("));
819

Nikolai Kosjar's avatar
Nikolai Kosjar committed
820
821
822
        const int rExprStart = currentFile->startOf(condition->right_expression);
        changes.move(rExprStart, currentFile->startOf(pattern->rparen_token), insertPos);
        changes.insert(insertPos, QLatin1String(")"));
823

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
827
828
829
830
831
832
        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();
833
834
835
    }

private:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
836
837
838
    IfStatementAST *pattern;
    BinaryExpressionAST *condition;
};
839

840
841
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
842
843
844
845
void SplitIfStatement::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
{
    IfStatementAST *pattern = 0;
    const QList<AST *> &path = interface->path();
846

Nikolai Kosjar's avatar
Nikolai Kosjar committed
847
848
849
850
851
852
    int index = path.size() - 1;
    for (; index != -1; --index) {
        AST *node = path.at(index);
        if (IfStatementAST *stmt = node->asIfStatement()) {
            pattern = stmt;
            break;
853
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
854
    }
855

Nikolai Kosjar's avatar
Nikolai Kosjar committed
856
857
    if (! pattern || ! pattern->statement)
        return;
858

Nikolai Kosjar's avatar
Nikolai Kosjar committed
859
860
861
862
863
864
    unsigned splitKind = 0;
    for (++index; index < path.size(); ++index) {
        AST *node = path.at(index);
        BinaryExpressionAST *condition = node->asBinaryExpression();
        if (! condition)
            return;
865

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
868
869
870
871
872
873
874
875
876
877
        // 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;
878
879
        }

Nikolai Kosjar's avatar
Nikolai Kosjar committed
880
881
882
883
        if (interface->isCursorOn(condition->binary_op_token)) {
            result.append(QuickFixOperation::Ptr(
                new SplitIfStatementOp(interface, index, pattern, condition)));
            return;
884
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
885
886
    }
}
887

888
889
890
891
/* 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,
892
                                          const CppRefactoringFilePtr &file, Type *type,
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
                                          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;
915
        }
916
    }
917

918
919
920
921
922
923
924
925
    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;
926
927
928
929
                    }
                }
            }
        }
930
931
932
    }
    return literal;
}
933

934
935
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
/// 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)) {
967
968
            const QString newQuote((m_actions & Factory::SingleQuoteAction)
                                   ? QLatin1Char('\'') : QLatin1Char('"'));
Nikolai Kosjar's avatar
Nikolai Kosjar committed
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
            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('(');
1000
1001
            if (m_actions
                    & (Factory::TranslateQCoreApplicationAction | Factory::TranslateNoopAction)) {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
                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;
};

1019
1020
} // anonymous namespace

1021
void WrapStringLiteral::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
1022
1023
1024
1025
1026
1027
1028
{
    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
1029
    ExpressionAST *literal = analyze(path, file, &type, &enclosingFunction);
1030
    if (!literal || type == TypeNone)
1031
        return;
1032
1033
1034
    if ((type == TypeChar && enclosingFunction == "QLatin1Char")
        || isQtStringLiteral(enclosingFunction)
        || isQtStringTranslation(enclosingFunction))
1035
        return;
1036

1037
1038
1039
    const int priority = path.size() - 1; // very high priority
    if (type == TypeChar) {
        unsigned actions = EncloseInQLatin1CharAction;
Nikolai Kosjar's avatar
Nikolai Kosjar committed
1040
1041
1042
        QString description = msgQtStringLiteralDescription(replacement(actions));
        result << OperationPtr(new WrapStringLiteralOp(interface, priority, actions,
                                                             description, literal));
1043
1044
1045
1046
1047
1048
        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
1049
1050
                result << OperationPtr(new WrapStringLiteralOp(interface, priority, actions,
                                                                     description, literal));
1051
            }
1052
        }
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
    } 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(...)");
1064
1065
                result << OperationPtr(new WrapStringLiteralOp(interface, priority, actions,
                                                               description, literal));
1066
1067
1068
                actions &= ~EncloseInQLatin1CharAction;
                description = QApplication::translate("CppTools::QuickFix",
                              "Convert to Character Literal");
1069
1070
                result << OperationPtr(new WrapStringLiteralOp(interface, priority, actions,
                                                               description, literal));
1071
1072
1073
            }
        }
        actions = EncloseInQLatin1StringAction | objectiveCActions;
Nikolai Kosjar's avatar
Nikolai Kosjar committed
1074
1075
1076
1077
        result << OperationPtr(
            new WrapStringLiteralOp(interface, priority, actions,
                msgQtStringLiteralDescription(replacement(actions), 4),
                    literal));
1078
        actions = EncloseInQStringLiteralAction | objectiveCActions;
Nikolai Kosjar's avatar
Nikolai Kosjar committed
1079
1080
1081
        result << OperationPtr(
            new WrapStringLiteralOp(interface, priority, actions,
                msgQtStringLiteralDescription(replacement(actions), 5), literal));
1082
    }
1083
}
1084

1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
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();
}
1101

1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
/* 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();
}
1112

1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
/* 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();
}
1123

1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
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
1136
1137
void TranslateStringLiteral::match(const CppQuickFixInterface &interface,
                                   QuickFixOperations &result)
1138
{
Nikolai Kosjar's avatar
Nikolai Kosjar committed
1139
1140
1141
1142
1143
1144
1145
1146
1147
    // Initialize
    WrapStringLiteral::Type type = WrapStringLiteral::TypeNone;
    QByteArray enclosingFunction;
    const QList<AST *> &path = interface->path();
    CppRefactoringFilePtr file = interface->currentFile();
    ExpressionAST *literal = WrapStringLiteral::analyze(path, file, &type, &enclosingFunction);
    if (!literal || type != WrapStringLiteral::TypeString
       || isQtStringLiteral(enclosingFunction) || isQtStringTranslation(enclosingFunction))
        return;
1148

Nikolai Kosjar's avatar
Nikolai Kosjar committed
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159