cppquickfixes.cpp 72 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 "cppcompleteswitch.h"
33
#include "cppeditor.h"
34
#include "cppinsertdecldef.h"
35
#include "cppinsertqtpropertymembers.h"
Leandro Melo's avatar
Leandro Melo committed
36
#include "cppquickfixassistant.h"
37
38
39
40
41
42
43
44
45

#include <Literals.h>
#include <Name.h>
#include <Names.h>
#include <Symbol.h>
#include <Symbols.h>
#include <Token.h>
#include <TranslationUnit.h>

46
#include <cplusplus/CppRewriter.h>
47
48
49
#include <cplusplus/DependencyTable.h>
#include <cplusplus/Overview.h>
#include <cplusplus/TypeOfExpression.h>
50
#include <cpptools/cppclassesfilter.h>
51
#include <cpptools/cppcodestylesettings.h>
52
53
54
#include <cpptools/cpppointerdeclarationformatter.h>
#include <cpptools/cpptoolsconstants.h>
#include <cpptools/ModelManagerInterface.h>
55
#include <cpptools/symbolfinder.h>
56
#include <extensionsystem/pluginmanager.h>
Nikolai Kosjar's avatar
Nikolai Kosjar committed
57
#include <utils/changeset.h>
58
59
#include <utils/qtcassert.h>

60
#include <QApplication>
61
#include <QFileInfo>
Nikolai Kosjar's avatar
Nikolai Kosjar committed
62
#include <QSharedPointer>
63
64
#include <QTextBlock>
#include <QTextCursor>
65

Nikolai Kosjar's avatar
Nikolai Kosjar committed
66
67
#include <cctype>

68
69
using namespace CppEditor;
using namespace CppEditor::Internal;
Nikolai Kosjar's avatar
Nikolai Kosjar committed
70
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
110
111
112
static inline bool isQtStringLiteral(const QByteArray &id)
{
    return id == "QLatin1String" || id == "QLatin1Literal" || id == "QStringLiteral";
}

113
114
115
116
117
static inline bool isQtStringTranslation(const QByteArray &id)
{
    return id == "tr" || id == "trUtf8" || id == "translate" || id == "QT_TRANSLATE_NOOP";
}

Nikolai Kosjar's avatar
Nikolai Kosjar committed
118
class InverseLogicalComparisonOp: public CppQuickFixOperation
119
120
{
public:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
121
122
123
124
    InverseLogicalComparisonOp(const CppQuickFixInterface &interface, int priority,
                               BinaryExpressionAST *binary, Kind invertToken)
        : CppQuickFixOperation(interface, priority)
        , binary(binary), nested(0), negation(0)
125
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
126
127
128
        Token tok;
        tok.f.kind = invertToken;
        replacement = QLatin1String(tok.spell());
129

Nikolai Kosjar's avatar
Nikolai Kosjar committed
130
131
132
        // check for enclosing nested expression
        if (priority - 1 >= 0)
            nested = interface->path()[priority - 1]->asNestedExpression();
133

Nikolai Kosjar's avatar
Nikolai Kosjar committed
134
135
136
137
138
        // 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;
139
140
141
        }
    }

Nikolai Kosjar's avatar
Nikolai Kosjar committed
142
    QString description() const
143
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
144
145
        return QApplication::translate("CppTools::QuickFix", "Rewrite Using %1").arg(replacement);
    }
146

Nikolai Kosjar's avatar
Nikolai Kosjar committed
147
148
149
150
151
152
153
154
155
156
157
158
159
160
    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(")"));
161
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
162
163
164
165
        changes.replace(currentFile->range(binary->binary_op_token), replacement);
        currentFile->setChangeSet(changes);
        currentFile->apply();
    }
166

Nikolai Kosjar's avatar
Nikolai Kosjar committed
167
168
169
170
private:
    BinaryExpressionAST *binary;
    NestedExpressionAST *nested;
    UnaryExpressionAST *negation;
171

Nikolai Kosjar's avatar
Nikolai Kosjar committed
172
    QString replacement;
173
174
};

Nikolai Kosjar's avatar
Nikolai Kosjar committed
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
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;
    }
211

Nikolai Kosjar's avatar
Nikolai Kosjar committed
212
213
214
    result.append(CppQuickFixOperation::Ptr(
        new InverseLogicalComparisonOp(interface, index, binary, invertToken)));
}
215

Nikolai Kosjar's avatar
Nikolai Kosjar committed
216
class FlipLogicalOperandsOp: public CppQuickFixOperation
217
218
{
public:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
219
220
221
222
223
    FlipLogicalOperandsOp(const CppQuickFixInterface &interface, int priority,
                          BinaryExpressionAST *binary, QString replacement)
        : CppQuickFixOperation(interface)
        , binary(binary)
        , replacement(replacement)
224
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
225
226
        setPriority(priority);
    }
227

Nikolai Kosjar's avatar
Nikolai Kosjar committed
228
229
230
231
232
233
234
    QString description() const
    {
        if (replacement.isEmpty())
            return QApplication::translate("CppTools::QuickFix", "Swap Operands");
        else
            return QApplication::translate("CppTools::QuickFix", "Rewrite Using %1").arg(replacement);
    }
235

Nikolai Kosjar's avatar
Nikolai Kosjar committed
236
237
238
239
    void perform()
    {
        CppRefactoringChanges refactoring(snapshot());
        CppRefactoringFilePtr currentFile = refactoring.file(fileName());
240

Nikolai Kosjar's avatar
Nikolai Kosjar committed
241
242
243
244
        ChangeSet changes;
        changes.flip(currentFile->range(binary->left_expression), currentFile->range(binary->right_expression));
        if (! replacement.isEmpty())
            changes.replace(currentFile->range(binary->binary_op_token), replacement);
245

Nikolai Kosjar's avatar
Nikolai Kosjar committed
246
247
        currentFile->setChangeSet(changes);
        currentFile->apply();
248
249
250
    }

private:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
251
252
253
    BinaryExpressionAST *binary;
    QString replacement;
};
254

Nikolai Kosjar's avatar
Nikolai Kosjar committed
255
256
257
258
void FlipLogicalOperands::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
{
    const QList<AST *> &path = interface->path();
    CppRefactoringFilePtr file = interface->currentFile();
259

Nikolai Kosjar's avatar
Nikolai Kosjar committed
260
261
262
263
264
265
    int index = path.size() - 1;
    BinaryExpressionAST *binary = path.at(index)->asBinaryExpression();
    if (! binary)
        return;
    if (! interface->isCursorOn(binary->binary_op_token))
        return;
266

Nikolai Kosjar's avatar
Nikolai Kosjar committed
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
    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;
    }
290

Nikolai Kosjar's avatar
Nikolai Kosjar committed
291
292
293
294
295
296
    QString replacement;
    if (flipToken != T_EOF_SYMBOL) {
        Token tok;
        tok.f.kind = flipToken;
        replacement = QLatin1String(tok.spell());
    }
297

Nikolai Kosjar's avatar
Nikolai Kosjar committed
298
299
300
    result.append(QuickFixOperation::Ptr(
        new FlipLogicalOperandsOp(interface, index, binary, replacement)));
}
301

Nikolai Kosjar's avatar
Nikolai Kosjar committed
302
class RewriteLogicalAndOp: public CppQuickFixOperation
303
304
{
public:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
305
306
307
308
309
310
311
312
    QSharedPointer<ASTPatternBuilder> mk;
    UnaryExpressionAST *left;
    UnaryExpressionAST *right;
    BinaryExpressionAST *pattern;

    RewriteLogicalAndOp(const CppQuickFixInterface &interface)
        : CppQuickFixOperation(interface)
        , mk(new ASTPatternBuilder)
313
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
314
315
316
317
        left = mk->UnaryExpression();
        right = mk->UnaryExpression();
        pattern = mk->BinaryExpression(left, right);
    }
318

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
339
340
341
342
343
void RewriteLogicalAnd::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
{
    BinaryExpressionAST *expression = 0;
    const QList<AST *> &path = interface->path();
    CppRefactoringFilePtr file = interface->currentFile();
344

Nikolai Kosjar's avatar
Nikolai Kosjar committed
345
346
347
348
349
    int index = path.size() - 1;
    for (; index != -1; --index) {
        expression = path.at(index)->asBinaryExpression();
        if (expression)
            break;
350
351
    }

Nikolai Kosjar's avatar
Nikolai Kosjar committed
352
353
    if (! expression)
        return;
354

Nikolai Kosjar's avatar
Nikolai Kosjar committed
355
356
    if (! interface->isCursorOn(expression->binary_op_token))
        return;
357

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
360
361
362
363
364
365
366
367
368
    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)) {
        op->setDescription(QApplication::translate("CppTools::QuickFix", "Rewrite Condition Using ||"));
        op->setPriority(index);
        result.append(op);
    }
}
369

Nikolai Kosjar's avatar
Nikolai Kosjar committed
370
371
372
373
bool SplitSimpleDeclaration::checkDeclaration(SimpleDeclarationAST *declaration)
{
    if (! declaration->semicolon_token)
        return false;
374

Nikolai Kosjar's avatar
Nikolai Kosjar committed
375
376
    if (! declaration->decl_specifier_list)
        return false;
377

Nikolai Kosjar's avatar
Nikolai Kosjar committed
378
379
    for (SpecifierListAST *it = declaration->decl_specifier_list; it; it = it->next) {
        SpecifierAST *specifier = it->value;
380

Nikolai Kosjar's avatar
Nikolai Kosjar committed
381
        if (specifier->asEnumSpecifier() != 0)
382
383
            return false;

Nikolai Kosjar's avatar
Nikolai Kosjar committed
384
        else if (specifier->asClassSpecifier() != 0)
385
            return false;
Nikolai Kosjar's avatar
Nikolai Kosjar committed
386
    }
387

Nikolai Kosjar's avatar
Nikolai Kosjar committed
388
389
    if (! declaration->declarator_list)
        return false;
390

Nikolai Kosjar's avatar
Nikolai Kosjar committed
391
392
    else if (! declaration->declarator_list->next)
        return false;
393

Nikolai Kosjar's avatar
Nikolai Kosjar committed
394
395
    return true;
}
396

Nikolai Kosjar's avatar
Nikolai Kosjar committed
397
398
399
400
401
402
403
404
405
406
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"));
407
408
    }

Nikolai Kosjar's avatar
Nikolai Kosjar committed
409
    void perform()
410
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
411
412
        CppRefactoringChanges refactoring(snapshot());
        CppRefactoringFilePtr currentFile = refactoring.file(fileName());
413

Nikolai Kosjar's avatar
Nikolai Kosjar committed
414
        ChangeSet changes;
415

Nikolai Kosjar's avatar
Nikolai Kosjar committed
416
417
418
419
        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);
420

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
423
424
        for (DeclaratorListAST *it = declaration->declarator_list->next; it; it = it->next) {
            DeclaratorAST *declarator = it->value;
425

Nikolai Kosjar's avatar
Nikolai Kosjar committed
426
427
428
429
430
            changes.insert(insertPos, QLatin1String("\n"));
            changes.copy(declSpecifiersStart, declSpecifiersEnd, insertPos);
            changes.insert(insertPos, QLatin1String(" "));
            changes.move(currentFile->range(declarator), insertPos);
            changes.insert(insertPos, QLatin1String(";"));
431

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
435
            prevDeclarator = declarator;
436
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
437
438
439
440

        currentFile->setChangeSet(changes);
        currentFile->appendIndentRange(currentFile->range(declaration));
        currentFile->apply();
441
442
443
    }

private:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
444
445
    SimpleDeclarationAST *declaration;
};
446

Nikolai Kosjar's avatar
Nikolai Kosjar committed
447
448
449
450
451
452
453
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();
454

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
458
459
        if (CoreDeclaratorAST *coreDecl = node->asCoreDeclarator())
            core_declarator = coreDecl;
460

Nikolai Kosjar's avatar
Nikolai Kosjar committed
461
462
463
        else if (SimpleDeclarationAST *simpleDecl = node->asSimpleDeclaration()) {
            if (checkDeclaration(simpleDecl)) {
                SimpleDeclarationAST *declaration = simpleDecl;
464

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
468
469
470
471
472
473
                if (cursorPosition >= startOfDeclSpecifier && cursorPosition <= endOfDeclSpecifier) {
                    // the AST node under cursor is a specifier.
                    result.append(QuickFixOperation::Ptr(
                        new SplitSimpleDeclarationOp(interface, index, declaration)));
                    return;
                }
474

Nikolai Kosjar's avatar
Nikolai Kosjar committed
475
476
477
478
479
480
                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;
                }
481
482
            }

Nikolai Kosjar's avatar
Nikolai Kosjar committed
483
            return;
484
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
485
486
    }
}
487

Nikolai Kosjar's avatar
Nikolai Kosjar committed
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
class AddBracesToIfOp: public CppQuickFixOperation
{
public:
    AddBracesToIfOp(const CppQuickFixInterface &interface, int priority, StatementAST *statement)
        : CppQuickFixOperation(interface, priority)
        , _statement(statement)
    {
        setDescription(QApplication::translate("CppTools::QuickFix",
                                               "Add Curly Braces"));
    }

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

        ChangeSet changes;
505

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

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
512
513
514
515
516
517
518
519
520
521
        currentFile->setChangeSet(changes);
        currentFile->appendIndentRange(Utils::ChangeSet::Range(start, end));
        currentFile->apply();
    }

private:
    StatementAST *_statement;
};

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
525
526
527
528
529
530
531
532
533
534
535
536
537
    // 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) {
538
        IfStatementAST *ifStatement = path.at(index)->asIfStatement();
Nikolai Kosjar's avatar
Nikolai Kosjar committed
539
540
        if (ifStatement && ifStatement->statement
            && interface->isCursorOn(ifStatement->statement)
541
            && ! ifStatement->statement->asCompoundStatement()) {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
542
543
            result.append(QuickFixOperation::Ptr(
                new AddBracesToIfOp(interface, index, ifStatement->statement)));
544
            return;
545
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
546
    }
547

Nikolai Kosjar's avatar
Nikolai Kosjar committed
548
549
550
    // ### This could very well be extended to the else branch
    // and other nodes entirely.
}
551

Nikolai Kosjar's avatar
Nikolai Kosjar committed
552
553
554
555
556
557
558
559
560
561
562
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);
563
564
    }

Nikolai Kosjar's avatar
Nikolai Kosjar committed
565
    void perform()
566
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
567
568
        CppRefactoringChanges refactoring(snapshot());
        CppRefactoringFilePtr currentFile = refactoring.file(fileName());
569

Nikolai Kosjar's avatar
Nikolai Kosjar committed
570
        ChangeSet changes;
571

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
574
575
576
        int insertPos = currentFile->startOf(pattern);
        changes.move(currentFile->range(condition), insertPos);
        changes.insert(insertPos, QLatin1String(";\n"));
577

Nikolai Kosjar's avatar
Nikolai Kosjar committed
578
579
580
581
        currentFile->setChangeSet(changes);
        currentFile->appendIndentRange(currentFile->range(pattern));
        currentFile->apply();
    }
582

Nikolai Kosjar's avatar
Nikolai Kosjar committed
583
584
585
586
587
588
    ASTMatcher matcher;
    ASTPatternBuilder mk;
    CPPEditorWidget *editor;
    ConditionAST *condition;
    IfStatementAST *pattern;
    CoreDeclaratorAST *core;
589
590
};

Nikolai Kosjar's avatar
Nikolai Kosjar committed
591
592
void MoveDeclarationOutOfIf::match(const CppQuickFixInterface &interface,
                                   QuickFixOperations &result)
593
{
Nikolai Kosjar's avatar
Nikolai Kosjar committed
594
595
596
597
598
599
600
601
602
603
604
605
    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;
606

Nikolai Kosjar's avatar
Nikolai Kosjar committed
607
608
609
610
                if (interface->isCursorOn(op->core)) {
                    op->setPriority(index);
                    result.append(op);
                    return;
611
612
613
614
                }
            }
        }
    }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
615
}
616

Nikolai Kosjar's avatar
Nikolai Kosjar committed
617
618
619
620
621
class MoveDeclarationOutOfWhileOp: public CppQuickFixOperation
{
public:
    MoveDeclarationOutOfWhileOp(const CppQuickFixInterface &interface)
        : CppQuickFixOperation(interface)
622
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
623
624
        setDescription(QApplication::translate("CppTools::QuickFix",
                                               "Move Declaration out of Condition"));
625

Nikolai Kosjar's avatar
Nikolai Kosjar committed
626
627
628
        condition = mk.Condition();
        pattern = mk.WhileStatement(condition);
    }
629

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
635
        ChangeSet changes;
636

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
640
641
642
643
644
        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"));
645

Nikolai Kosjar's avatar
Nikolai Kosjar committed
646
647
648
649
        currentFile->setChangeSet(changes);
        currentFile->appendIndentRange(currentFile->range(pattern));
        currentFile->apply();
    }
650

Nikolai Kosjar's avatar
Nikolai Kosjar committed
651
652
653
654
655
656
657
    ASTMatcher matcher;
    ASTPatternBuilder mk;
    CPPEditorWidget *editor;
    ConditionAST *condition;
    WhileStatementAST *pattern;
    CoreDeclaratorAST *core;
};
658

Nikolai Kosjar's avatar
Nikolai Kosjar committed
659
660
void MoveDeclarationOutOfWhile::match(const CppQuickFixInterface &interface,
                                      QuickFixOperations &result)
661
{
Nikolai Kosjar's avatar
Nikolai Kosjar committed
662
663
    const QList<AST *> &path = interface->path();
    QSharedPointer<MoveDeclarationOutOfWhileOp> op(new MoveDeclarationOutOfWhileOp(interface));
664

Nikolai Kosjar's avatar
Nikolai Kosjar committed
665
666
667
668
669
670
    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;
671

Nikolai Kosjar's avatar
Nikolai Kosjar committed
672
673
                if (! op->core)
                    return;
674

Nikolai Kosjar's avatar
Nikolai Kosjar committed
675
676
                if (! declarator->equal_token)
                    return;
677

Nikolai Kosjar's avatar
Nikolai Kosjar committed
678
679
                if (! declarator->initializer)
                    return;
680

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
691
692
693
694
695
696
697
698
class SplitIfStatementOp: public CppQuickFixOperation
{
public:
    SplitIfStatementOp(const CppQuickFixInterface &interface, int priority,
                       IfStatementAST *pattern, BinaryExpressionAST *condition)
        : CppQuickFixOperation(interface, priority)
        , pattern(pattern)
        , condition(condition)
699
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
700
701
702
703
704
705
706
707
        setDescription(QApplication::translate("CppTools::QuickFix",
                                               "Split if Statement"));
    }

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

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
711
712
713
714
715
        if (binaryToken.is(T_AMPER_AMPER))
            splitAndCondition(currentFile);
        else
            splitOrCondition(currentFile);
    }
716

Nikolai Kosjar's avatar
Nikolai Kosjar committed
717
718
719
    void splitAndCondition(CppRefactoringFilePtr currentFile) const
    {
        ChangeSet changes;
720

Nikolai Kosjar's avatar
Nikolai Kosjar committed
721
722
723
724
        int startPos = currentFile->startOf(pattern);
        changes.insert(startPos, QLatin1String("if ("));
        changes.move(currentFile->range(condition->left_expression), startPos);
        changes.insert(startPos, QLatin1String(") {\n"));
725

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
730
731
732
733
        currentFile->setChangeSet(changes);
        currentFile->appendIndentRange(currentFile->range(pattern));
        currentFile->apply();
    }
734

Nikolai Kosjar's avatar
Nikolai Kosjar committed
735
    void splitOrCondition(CppRefactoringFilePtr currentFile) const
736
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
737
        ChangeSet changes;
738

Nikolai Kosjar's avatar
Nikolai Kosjar committed
739
740
        StatementAST *ifTrueStatement = pattern->statement;
        CompoundStatementAST *compoundStatement = ifTrueStatement->asCompoundStatement();
741

Nikolai Kosjar's avatar
Nikolai Kosjar committed
742
743
744
745
746
747
        int insertPos = currentFile->endOf(ifTrueStatement);
        if (compoundStatement)
            changes.insert(insertPos, QLatin1String(" "));
        else
            changes.insert(insertPos, QLatin1String("\n"));
        changes.insert(insertPos, QLatin1String("else if ("));
748

Nikolai Kosjar's avatar
Nikolai Kosjar committed
749
750
751
        const int rExprStart = currentFile->startOf(condition->right_expression);
        changes.move(rExprStart, currentFile->startOf(pattern->rparen_token), insertPos);
        changes.insert(insertPos, QLatin1String(")"));
752

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
756
757
758
759
760
761
        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();
762
763
764
    }

private:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
765
766
767
    IfStatementAST *pattern;
    BinaryExpressionAST *condition;
};
768

Nikolai Kosjar's avatar
Nikolai Kosjar committed
769
770
771
772
void SplitIfStatement::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
{
    IfStatementAST *pattern = 0;
    const QList<AST *> &path = interface->path();
773

Nikolai Kosjar's avatar
Nikolai Kosjar committed
774
775
776
777
778
779
    int index = path.size() - 1;
    for (; index != -1; --index) {
        AST *node = path.at(index);
        if (IfStatementAST *stmt = node->asIfStatement()) {
            pattern = stmt;
            break;
780
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
781
    }
782

Nikolai Kosjar's avatar
Nikolai Kosjar committed
783
784
    if (! pattern || ! pattern->statement)
        return;
785

Nikolai Kosjar's avatar
Nikolai Kosjar committed
786
787
788
789
790
791
    unsigned splitKind = 0;
    for (++index; index < path.size(); ++index) {
        AST *node = path.at(index);
        BinaryExpressionAST *condition = node->asBinaryExpression();
        if (! condition)
            return;
792

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
795
796
797
798
799
800
801
802
803
804
        // 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;
805
806
        }

Nikolai Kosjar's avatar
Nikolai Kosjar committed
807
808
809
810
        if (interface->isCursorOn(condition->binary_op_token)) {
            result.append(QuickFixOperation::Ptr(
                new SplitIfStatementOp(interface, index, pattern, condition)));
            return;
811
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
812
813
    }
}
814
815
816
817
818
819
820

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

821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
static inline QString msgQtStringLiteralDescription(const QString &replacement)
{
    return QApplication::translate("CppTools::QuickFix", "Enclose in %1(...)").arg(replacement);
}

/* 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,
                                          const CppRefactoringFilePtr &file,
                                          Type *type,
                                          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;
854
        }
855
    }
856

857
858
859
860
861
862
863
864
    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;
865
866
867
868
                    }
                }
            }
        }
869
870
871
    }
    return literal;
}
872

Nikolai Kosjar's avatar
Nikolai Kosjar committed
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
/// 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)) {
            const QString newQuote((m_actions & Factory::SingleQuoteAction) ? QLatin1Char('\'') : QLatin1Char('"'));
            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('(');
            if (m_actions & (Factory::TranslateQCoreApplicationAction | Factory::TranslateNoopAction)) {
                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;
};

954
void WrapStringLiteral::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
955
956
957
958
959
960
961
{
    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
962
    ExpressionAST *literal = analyze(path, file, &type, &enclosingFunction);
963
    if (!literal || type == TypeNone)
964
        return;
965
966
967
    if ((type == TypeChar && enclosingFunction == "QLatin1Char")
        || isQtStringLiteral(enclosingFunction)
        || isQtStringTranslation(enclosingFunction))
968
        return;
969

970
971
972
    const int priority = path.size() - 1; // very high priority
    if (type == TypeChar) {
        unsigned actions = EncloseInQLatin1CharAction;
Nikolai Kosjar's avatar
Nikolai Kosjar committed
973
974
975
        QString description = msgQtStringLiteralDescription(replacement(actions));
        result << OperationPtr(new WrapStringLiteralOp(interface, priority, actions,
                                                             description, literal));
976
977
978
979
980
981
        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
982
983
                result << OperationPtr(new WrapStringLiteralOp(interface, priority, actions,
                                                                     description, literal));
984
            }
985
        }
986
987
988
989
990
991
992
993
994
995
996
    } 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(...)");
Nikolai Kosjar's avatar
Nikolai Kosjar committed
997
998
                result << OperationPtr(new WrapStringLiteralOp(interface, priority,
                                                                     actions, description, literal));
999
1000
1001
                actions &= ~EncloseInQLatin1CharAction;
                description = QApplication::translate("CppTools::QuickFix",
                              "Convert to Character Literal");
Nikolai Kosjar's avatar
Nikolai Kosjar committed
1002
1003
                result << OperationPtr(new WrapStringLiteralOp(interface, priority,
                                                                     actions, description, literal));
1004
1005
1006
            }
        }
        actions = EncloseInQLatin1StringAction | objectiveCActions;
Nikolai Kosjar's avatar
Nikolai Kosjar committed
1007
1008
1009
1010
        result << OperationPtr(
            new WrapStringLiteralOp(interface, priority, actions,
                msgQtStringLiteralDescription(replacement(actions), 4),
                    literal));
1011
        actions = EncloseInQStringLiteralAction | objectiveCActions;
Nikolai Kosjar's avatar
Nikolai Kosjar committed
1012
1013
1014
        result << OperationPtr(
            new WrapStringLiteralOp(interface, priority, actions,
                msgQtStringLiteralDescription(replacement(actions), 5), literal));
1015
    }
1016
}
1017

1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
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();
}
1034

1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
/* 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();
}
1045

1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
/* 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();
}
1056

Nikolai Kosjar's avatar
Nikolai Kosjar committed
1057
1058
void TranslateStringLiteral::match(const CppQuickFixInterface &interface,
                                   QuickFixOperations &result)
1059
{
Nikolai Kosjar's avatar
Nikolai Kosjar committed
1060
1061
1062
1063
1064
1065
1066
1067
1068
    // 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;
1069

Nikolai Kosjar's avatar
Nikolai Kosjar committed
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
    QString trContext;

    QSharedPointer<Control> control = interface->context().control();
    const Name *trName = control->identifier("tr");

    // Check whether we are in a method:
    const QString description = QApplication::translate("CppTools::QuickFix", "Mark as Translatable");
    for (int i = path.size() - 1; i >= 0; --i) {
        if (FunctionDefinitionAST *definition = path.at(i)->asFunctionDefinition()) {
            Function *function = definition->symbol;
            ClassOrNamespace *b = interface->context().lookupType(function);
            if (b) {
                // Do we have a tr method?
                foreach (const LookupItem &r, b->find(trName)) {
                    Symbol *s = r.declaration();
                    if (s->type()->isFunctionType()) {
                        // no context required for tr
                        result.append(QuickFixOperation::Ptr(
                            new WrapStringLiteralOp(interface, path.size() - 1,
                                                          WrapStringLiteral::TranslateTrAction,
                                                          description, literal)));
                        return;
                    }
                }
            }
            // We need to do a QCA::translate, so we need a context.
            // Use fully qualified class name:
            Overview oo;
            foreach (const Name *n, LookupContext::path(function)) {
                if (!trContext.isEmpty())
                    trContext.append(QLatin1String("::"));
                trContext.append(oo.prettyName(n));
            }
            // ... or global if none available!
            if (trContext.isEmpty())
                trContext = QLatin1String("GLOBAL");
            result.append(QuickFixOperation::Ptr(
                new WrapStringLiteralOp(interface, path.size() - 1,
                                              WrapStringLiteral::TranslateQCoreApplicationAction,
                                              description, literal, trContext)));
            return;
1111
1112
1113
        }
    }

Nikolai Kosjar's avatar
Nikolai Kosjar committed
1114
1115
1116
1117
1118
    // We need to use Q_TRANSLATE_NOOP
    result.append(QuickFixOperation::Ptr(
        new WrapStringLiteralOp(interface, path.size() - 1,
                                      WrapStringLiteral::TranslateNoopAction,
                                      description, literal, trContext)));
1119
}
1120

Nikolai Kosjar's avatar
Nikolai Kosjar committed
1121
class ConvertCStringToNSStringOp: public CppQuickFixOperation
1122
1123
{
public:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
1124
1125
1126
1127
1128
    ConvertCStringToNSStringOp(const CppQuickFixInterface &interface, int priority,
                               StringLiteralAST *stringLiteral, CallAST *qlatin1Call)
        : CppQuickFixOperation(interface, priority)
        , stringLiteral(stringLiteral)
        , qlatin1Call(qlatin1Call)
1129
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
1130
1131
        setDescription(QApplication::translate("CppTools::QuickFix",
                                               "Convert to Objective-C String Literal"));
1132
1133
    }

Nikolai Kosjar's avatar
Nikolai Kosjar committed
1134
    void perform()
1135
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
1136
1137
        CppRefactoringChanges refactoring(snapshot());
        CppRefactoringFilePtr currentFile = refactoring.file(fileName());
1138

Nikolai Kosjar's avatar
Nikolai Kosjar committed
1139
        ChangeSet changes;
1140

Nikolai Kosjar's avatar
Nikolai Kosjar committed
1141
1142
1143
1144
1145
1146
        if (qlatin1Call) {
            changes.replace(currentFile->startOf(qlatin1Call), currentFile->startOf(stringLiteral), QLatin1String("@"));
            changes.remove(currentFile->endOf(stringLiteral), currentFile->endOf(qlatin1Call));
        } else {
            changes.insert(currentFile->startOf(stringLiteral), QLatin1String("@"));
        }
1147

Nikolai Kosjar's avatar
Nikolai Kosjar committed
1148
1149
        currentFile->setChangeSet(changes);
        currentFile->apply();
1150
1151
1152
    }

private:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
1153
1154
1155