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