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

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

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

36 37
#include <coreplugin/icore.h>

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

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

52
#include <extensionsystem/pluginmanager.h>
53

54 55 56
#include <texteditor/fontsettings.h>
#include <texteditor/texteditorsettings.h>

57 58
#include <utils/qtcassert.h>

59
#include <QApplication>
60 61 62
#include <QCheckBox>
#include <QComboBox>
#include <QDialogButtonBox>
63
#include <QDir>
64
#include <QFileInfo>
65 66
#include <QGroupBox>
#include <QHBoxLayout>
67
#include <QInputDialog>
68 69
#include <QItemDelegate>
#include <QLabel>
70
#include <QMessageBox>
71 72 73
#include <QPointer>
#include <QPushButton>
#include <QQueue>
Nikolai Kosjar's avatar
Nikolai Kosjar committed
74
#include <QSharedPointer>
75 76
#include <QSortFilterProxyModel>
#include <QStandardItemModel>
77 78
#include <QTextBlock>
#include <QTextCursor>
79 80
#include <QTreeView>
#include <QVBoxLayout>
81

Nikolai Kosjar's avatar
Nikolai Kosjar committed
82 83
#include <cctype>

84
using namespace CPlusPlus;
85 86
using namespace CppEditor;
using namespace CppEditor::Internal;
87 88 89
using namespace CppTools;
using namespace TextEditor;
using namespace Utils;
Nikolai Kosjar's avatar
Nikolai Kosjar committed
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125

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);
126 127 128

    plugIn->addAutoReleasedObject(new MoveFuncDefOutside);
    plugIn->addAutoReleasedObject(new MoveFuncDefToDecl);
129 130

    plugIn->addAutoReleasedObject(new AssignToLocalVariable);
131 132

    plugIn->addAutoReleasedObject(new InsertVirtualMethods);
Nikolai Kosjar's avatar
Nikolai Kosjar committed
133
}
134

135 136 137 138
// In the following anonymous namespace all functions are collected, which could be of interest for
// different quick fixes.
namespace {

139 140 141 142 143 144
enum DefPos {
    DefPosInsideClass,
    DefPosOutsideClass,
    DefPosImplementationFile
};

145
inline bool isQtStringLiteral(const QByteArray &id)
146 147 148 149
{
    return id == "QLatin1String" || id == "QLatin1Literal" || id == "QStringLiteral";
}

150
inline bool isQtStringTranslation(const QByteArray &id)
151 152 153 154
{
    return id == "tr" || id == "trUtf8" || id == "translate" || id == "QT_TRANSLATE_NOOP";
}

155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
Class *isMemberFunction(const LookupContext &context, Function *function)
{
    QTC_ASSERT(function, return 0);

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

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

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

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

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

    return 0;
}

} // anonymous namespace

namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
189
class InverseLogicalComparisonOp: public CppQuickFixOperation
190 191
{
public:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
192 193 194 195
    InverseLogicalComparisonOp(const CppQuickFixInterface &interface, int priority,
                               BinaryExpressionAST *binary, Kind invertToken)
        : CppQuickFixOperation(interface, priority)
        , binary(binary), nested(0), negation(0)
196
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
197 198 199
        Token tok;
        tok.f.kind = invertToken;
        replacement = QLatin1String(tok.spell());
200

Nikolai Kosjar's avatar
Nikolai Kosjar committed
201 202 203
        // check for enclosing nested expression
        if (priority - 1 >= 0)
            nested = interface->path()[priority - 1]->asNestedExpression();
204

Nikolai Kosjar's avatar
Nikolai Kosjar committed
205 206 207 208 209
        // 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;
210 211 212
        }
    }

Nikolai Kosjar's avatar
Nikolai Kosjar committed
213
    QString description() const
214
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
215 216
        return QApplication::translate("CppTools::QuickFix", "Rewrite Using %1").arg(replacement);
    }
217

Nikolai Kosjar's avatar
Nikolai Kosjar committed
218 219 220 221 222 223 224 225 226 227 228 229 230 231
    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(")"));
232
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
233 234 235 236
        changes.replace(currentFile->range(binary->binary_op_token), replacement);
        currentFile->setChangeSet(changes);
        currentFile->apply();
    }
237

Nikolai Kosjar's avatar
Nikolai Kosjar committed
238 239 240 241
private:
    BinaryExpressionAST *binary;
    NestedExpressionAST *nested;
    UnaryExpressionAST *negation;
242

Nikolai Kosjar's avatar
Nikolai Kosjar committed
243
    QString replacement;
244 245
};

246 247
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
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;
    }
284

Nikolai Kosjar's avatar
Nikolai Kosjar committed
285 286 287
    result.append(CppQuickFixOperation::Ptr(
        new InverseLogicalComparisonOp(interface, index, binary, invertToken)));
}
288

289 290
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
291
class FlipLogicalOperandsOp: public CppQuickFixOperation
292 293
{
public:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
294 295 296 297 298
    FlipLogicalOperandsOp(const CppQuickFixInterface &interface, int priority,
                          BinaryExpressionAST *binary, QString replacement)
        : CppQuickFixOperation(interface)
        , binary(binary)
        , replacement(replacement)
299
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
300 301
        setPriority(priority);
    }
302

Nikolai Kosjar's avatar
Nikolai Kosjar committed
303 304 305 306 307 308 309
    QString description() const
    {
        if (replacement.isEmpty())
            return QApplication::translate("CppTools::QuickFix", "Swap Operands");
        else
            return QApplication::translate("CppTools::QuickFix", "Rewrite Using %1").arg(replacement);
    }
310

Nikolai Kosjar's avatar
Nikolai Kosjar committed
311 312 313 314
    void perform()
    {
        CppRefactoringChanges refactoring(snapshot());
        CppRefactoringFilePtr currentFile = refactoring.file(fileName());
315

Nikolai Kosjar's avatar
Nikolai Kosjar committed
316
        ChangeSet changes;
317 318
        changes.flip(currentFile->range(binary->left_expression),
                     currentFile->range(binary->right_expression));
Nikolai Kosjar's avatar
Nikolai Kosjar committed
319 320
        if (! replacement.isEmpty())
            changes.replace(currentFile->range(binary->binary_op_token), replacement);
321

Nikolai Kosjar's avatar
Nikolai Kosjar committed
322 323
        currentFile->setChangeSet(changes);
        currentFile->apply();
324 325 326
    }

private:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
327 328 329
    BinaryExpressionAST *binary;
    QString replacement;
};
330

331 332
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
333 334 335 336
void FlipLogicalOperands::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
{
    const QList<AST *> &path = interface->path();
    CppRefactoringFilePtr file = interface->currentFile();
337

Nikolai Kosjar's avatar
Nikolai Kosjar committed
338 339 340 341 342 343
    int index = path.size() - 1;
    BinaryExpressionAST *binary = path.at(index)->asBinaryExpression();
    if (! binary)
        return;
    if (! interface->isCursorOn(binary->binary_op_token))
        return;
344

Nikolai Kosjar's avatar
Nikolai Kosjar committed
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
    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;
    }
368

Nikolai Kosjar's avatar
Nikolai Kosjar committed
369 370 371 372 373 374
    QString replacement;
    if (flipToken != T_EOF_SYMBOL) {
        Token tok;
        tok.f.kind = flipToken;
        replacement = QLatin1String(tok.spell());
    }
375

Nikolai Kosjar's avatar
Nikolai Kosjar committed
376 377 378
    result.append(QuickFixOperation::Ptr(
        new FlipLogicalOperandsOp(interface, index, binary, replacement)));
}
379

380 381
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
382
class RewriteLogicalAndOp: public CppQuickFixOperation
383 384
{
public:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
385 386 387 388 389 390 391 392
    QSharedPointer<ASTPatternBuilder> mk;
    UnaryExpressionAST *left;
    UnaryExpressionAST *right;
    BinaryExpressionAST *pattern;

    RewriteLogicalAndOp(const CppQuickFixInterface &interface)
        : CppQuickFixOperation(interface)
        , mk(new ASTPatternBuilder)
393
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
394 395 396 397
        left = mk->UnaryExpression();
        right = mk->UnaryExpression();
        pattern = mk->BinaryExpression(left, right);
    }
398

Nikolai Kosjar's avatar
Nikolai Kosjar committed
399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417
    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();
    }
};
418

419 420
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
421 422 423 424 425
void RewriteLogicalAnd::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
{
    BinaryExpressionAST *expression = 0;
    const QList<AST *> &path = interface->path();
    CppRefactoringFilePtr file = interface->currentFile();
426

Nikolai Kosjar's avatar
Nikolai Kosjar committed
427 428 429 430 431
    int index = path.size() - 1;
    for (; index != -1; --index) {
        expression = path.at(index)->asBinaryExpression();
        if (expression)
            break;
432 433
    }

Nikolai Kosjar's avatar
Nikolai Kosjar committed
434 435
    if (! expression)
        return;
436

Nikolai Kosjar's avatar
Nikolai Kosjar committed
437 438
    if (! interface->isCursorOn(expression->binary_op_token))
        return;
439

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
442 443 444 445
    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)) {
446 447
        op->setDescription(QApplication::translate("CppTools::QuickFix",
                                                   "Rewrite Condition Using ||"));
Nikolai Kosjar's avatar
Nikolai Kosjar committed
448 449 450 451
        op->setPriority(index);
        result.append(op);
    }
}
452

Nikolai Kosjar's avatar
Nikolai Kosjar committed
453 454 455 456
bool SplitSimpleDeclaration::checkDeclaration(SimpleDeclarationAST *declaration)
{
    if (! declaration->semicolon_token)
        return false;
457

Nikolai Kosjar's avatar
Nikolai Kosjar committed
458 459
    if (! declaration->decl_specifier_list)
        return false;
460

Nikolai Kosjar's avatar
Nikolai Kosjar committed
461 462
    for (SpecifierListAST *it = declaration->decl_specifier_list; it; it = it->next) {
        SpecifierAST *specifier = it->value;
463

Nikolai Kosjar's avatar
Nikolai Kosjar committed
464
        if (specifier->asEnumSpecifier() != 0)
465 466
            return false;

Nikolai Kosjar's avatar
Nikolai Kosjar committed
467
        else if (specifier->asClassSpecifier() != 0)
468
            return false;
Nikolai Kosjar's avatar
Nikolai Kosjar committed
469
    }
470

Nikolai Kosjar's avatar
Nikolai Kosjar committed
471 472
    if (! declaration->declarator_list)
        return false;
473

Nikolai Kosjar's avatar
Nikolai Kosjar committed
474 475
    else if (! declaration->declarator_list->next)
        return false;
476

Nikolai Kosjar's avatar
Nikolai Kosjar committed
477 478
    return true;
}
479

480 481
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
482 483 484 485 486 487 488 489 490 491
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"));
492 493
    }

Nikolai Kosjar's avatar
Nikolai Kosjar committed
494
    void perform()
495
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
496 497
        CppRefactoringChanges refactoring(snapshot());
        CppRefactoringFilePtr currentFile = refactoring.file(fileName());
498

Nikolai Kosjar's avatar
Nikolai Kosjar committed
499
        ChangeSet changes;
500

Nikolai Kosjar's avatar
Nikolai Kosjar committed
501 502 503 504
        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);
505

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
508 509
        for (DeclaratorListAST *it = declaration->declarator_list->next; it; it = it->next) {
            DeclaratorAST *declarator = it->value;
510

Nikolai Kosjar's avatar
Nikolai Kosjar committed
511 512 513 514 515
            changes.insert(insertPos, QLatin1String("\n"));
            changes.copy(declSpecifiersStart, declSpecifiersEnd, insertPos);
            changes.insert(insertPos, QLatin1String(" "));
            changes.move(currentFile->range(declarator), insertPos);
            changes.insert(insertPos, QLatin1String(";"));
516

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
520
            prevDeclarator = declarator;
521
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
522 523 524 525

        currentFile->setChangeSet(changes);
        currentFile->appendIndentRange(currentFile->range(declaration));
        currentFile->apply();
526 527 528
    }

private:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
529 530
    SimpleDeclarationAST *declaration;
};
531

532 533
} // anonymous namespace

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

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
545 546
        if (CoreDeclaratorAST *coreDecl = node->asCoreDeclarator())
            core_declarator = coreDecl;
547

Nikolai Kosjar's avatar
Nikolai Kosjar committed
548 549 550
        else if (SimpleDeclarationAST *simpleDecl = node->asSimpleDeclaration()) {
            if (checkDeclaration(simpleDecl)) {
                SimpleDeclarationAST *declaration = simpleDecl;
551

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
555 556 557 558 559 560
                if (cursorPosition >= startOfDeclSpecifier && cursorPosition <= endOfDeclSpecifier) {
                    // the AST node under cursor is a specifier.
                    result.append(QuickFixOperation::Ptr(
                        new SplitSimpleDeclarationOp(interface, index, declaration)));
                    return;
                }
561

Nikolai Kosjar's avatar
Nikolai Kosjar committed
562 563 564 565 566 567
                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;
                }
568 569
            }

Nikolai Kosjar's avatar
Nikolai Kosjar committed
570
            return;
571
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
572 573
    }
}
574

575 576
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
577 578 579 580 581 582 583
class AddBracesToIfOp: public CppQuickFixOperation
{
public:
    AddBracesToIfOp(const CppQuickFixInterface &interface, int priority, StatementAST *statement)
        : CppQuickFixOperation(interface, priority)
        , _statement(statement)
    {
584
        setDescription(QApplication::translate("CppTools::QuickFix", "Add Curly Braces"));
Nikolai Kosjar's avatar
Nikolai Kosjar committed
585 586 587 588 589 590 591 592
    }

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

        ChangeSet changes;
593

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

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
600
        currentFile->setChangeSet(changes);
601
        currentFile->appendIndentRange(ChangeSet::Range(start, end));
Nikolai Kosjar's avatar
Nikolai Kosjar committed
602 603 604 605 606 607 608
        currentFile->apply();
    }

private:
    StatementAST *_statement;
};

609 610
} // anonymous namespace

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
615 616 617 618 619 620 621 622 623 624 625 626 627
    // 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) {
628
        IfStatementAST *ifStatement = path.at(index)->asIfStatement();
Nikolai Kosjar's avatar
Nikolai Kosjar committed
629 630
        if (ifStatement && ifStatement->statement
            && interface->isCursorOn(ifStatement->statement)
631
            && ! ifStatement->statement->asCompoundStatement()) {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
632 633
            result.append(QuickFixOperation::Ptr(
                new AddBracesToIfOp(interface, index, ifStatement->statement)));
634
            return;
635
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
636
    }
637

Nikolai Kosjar's avatar
Nikolai Kosjar committed
638 639 640
    // ### This could very well be extended to the else branch
    // and other nodes entirely.
}
641

642 643
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
644 645 646 647 648 649 650 651 652 653 654
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);
655 656
    }

Nikolai Kosjar's avatar
Nikolai Kosjar committed
657
    void perform()
658
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
659 660
        CppRefactoringChanges refactoring(snapshot());
        CppRefactoringFilePtr currentFile = refactoring.file(fileName());
661

Nikolai Kosjar's avatar
Nikolai Kosjar committed
662
        ChangeSet changes;
663

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
666 667 668
        int insertPos = currentFile->startOf(pattern);
        changes.move(currentFile->range(condition), insertPos);
        changes.insert(insertPos, QLatin1String(";\n"));
669

Nikolai Kosjar's avatar
Nikolai Kosjar committed
670 671 672 673
        currentFile->setChangeSet(changes);
        currentFile->appendIndentRange(currentFile->range(pattern));
        currentFile->apply();
    }
674

Nikolai Kosjar's avatar
Nikolai Kosjar committed
675 676 677 678 679 680
    ASTMatcher matcher;
    ASTPatternBuilder mk;
    CPPEditorWidget *editor;
    ConditionAST *condition;
    IfStatementAST *pattern;
    CoreDeclaratorAST *core;
681 682
};

683 684
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
685 686
void MoveDeclarationOutOfIf::match(const CppQuickFixInterface &interface,
                                   QuickFixOperations &result)
687
{
Nikolai Kosjar's avatar
Nikolai Kosjar committed
688 689 690 691 692 693 694 695 696 697 698 699
    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;
700

Nikolai Kosjar's avatar
Nikolai Kosjar committed
701 702 703 704
                if (interface->isCursorOn(op->core)) {
                    op->setPriority(index);
                    result.append(op);
                    return;
705 706 707 708
                }
            }
        }
    }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
709
}
710

711 712
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
713 714 715 716 717
class MoveDeclarationOutOfWhileOp: public CppQuickFixOperation
{
public:
    MoveDeclarationOutOfWhileOp(const CppQuickFixInterface &interface)
        : CppQuickFixOperation(interface)
718
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
719 720
        setDescription(QApplication::translate("CppTools::QuickFix",
                                               "Move Declaration out of Condition"));
721

Nikolai Kosjar's avatar
Nikolai Kosjar committed
722 723 724
        condition = mk.Condition();
        pattern = mk.WhileStatement(condition);
    }
725

Nikolai Kosjar's avatar
Nikolai Kosjar committed
726 727 728 729
    void perform()
    {
        CppRefactoringChanges refactoring(snapshot());
        CppRefactoringFilePtr currentFile = refactoring.file(fileName());
730

Nikolai Kosjar's avatar
Nikolai Kosjar committed
731
        ChangeSet changes;
732

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

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
742 743 744 745
        currentFile->setChangeSet(changes);
        currentFile->appendIndentRange(currentFile->range(pattern));
        currentFile->apply();
    }
746

Nikolai Kosjar's avatar
Nikolai Kosjar committed
747 748 749 750 751 752 753
    ASTMatcher matcher;
    ASTPatternBuilder mk;
    CPPEditorWidget *editor;
    ConditionAST *condition;
    WhileStatementAST *pattern;
    CoreDeclaratorAST *core;
};
754

755 756
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
757 758
void MoveDeclarationOutOfWhile::match(const CppQuickFixInterface &interface,
                                      QuickFixOperations &result)
759
{
Nikolai Kosjar's avatar
Nikolai Kosjar committed
760 761
    const QList<AST *> &path = interface->path();
    QSharedPointer<MoveDeclarationOutOfWhileOp> op(new MoveDeclarationOutOfWhileOp(interface));
762

Nikolai Kosjar's avatar
Nikolai Kosjar committed
763 764 765 766 767 768
    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;
769

Nikolai Kosjar's avatar
Nikolai Kosjar committed
770 771
                if (! op->core)
                    return;
772

Nikolai Kosjar's avatar
Nikolai Kosjar committed
773 774
                if (! declarator->equal_token)
                    return;
775

Nikolai Kosjar's avatar
Nikolai Kosjar committed
776 777
                if (! declarator->initializer)
                    return;
778

Nikolai Kosjar's avatar
Nikolai Kosjar committed
779 780 781 782
                if (interface->isCursorOn(op->core)) {
                    op->setPriority(index);
                    result.append(op);
                    return;
783 784 785 786
                }
            }
        }
    }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
787
}
788

789 790
namespace {

Nikolai Kosjar's avatar
Nikolai Kosjar committed
791 792 793 794 795 796 797 798
class SplitIfStatementOp: public CppQuickFixOperation
{
public:
    SplitIfStatementOp(const CppQuickFixInterface &interface, int priority,
                       IfStatementAST *pattern, BinaryExpressionAST *condition)
        : CppQuickFixOperation(interface, priority)
        , pattern(pattern)
        , condition(condition)
799
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
800 801 802 803 804 805 806 807
        setDescription(QApplication::translate("CppTools::QuickFix",
                                               "Split if Statement"));
    }

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

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
811 812 813 814 815
        if (binaryToken.is(T_AMPER_AMPER))
            splitAndCondition(currentFile);
        else
            splitOrCondition(currentFile);
    }
816

Nikolai Kosjar's avatar
Nikolai Kosjar committed
817 818 819
    void splitAndCondition(CppRefactoringFilePtr currentFile) const
    {
        ChangeSet changes;
820

Nikolai Kosjar's avatar
Nikolai Kosjar committed
821 822 823 824
        int startPos = currentFile->startOf(pattern);
        changes.insert(startPos, QLatin1String("if ("));
        changes.move(currentFile->range(condition->left_expression), startPos);
        changes.insert(startPos, QLatin1String(") {\n"));
825

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
830 831 832 833
        currentFile->setChangeSet(changes);
        currentFile->appendIndentRange(currentFile->range(pattern));
        currentFile->apply();
    }
834

Nikolai Kosjar's avatar
Nikolai Kosjar committed
835
    void splitOrCondition(CppRefactoringFilePtr currentFile) const
836
    {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
837
        ChangeSet changes;
838

Nikolai Kosjar's avatar
Nikolai Kosjar committed
839 840
        StatementAST *ifTrueStatement = pattern->statement;
        CompoundStatementAST *compoundStatement = ifTrueStatement->asCompoundStatement();
841

Nikolai Kosjar's avatar
Nikolai Kosjar committed
842 843 844 845 846 847
        int insertPos = currentFile->endOf(ifTrueStatement);
        if (compoundStatement)
            changes.insert(insertPos, QLatin1String(" "));
        else
            changes.insert(insertPos, QLatin1String("\n"));
        changes.insert(insertPos, QLatin1String("else if ("));
848

Nikolai Kosjar's avatar
Nikolai Kosjar committed
849 850 851
        const int rExprStart = currentFile->startOf(condition->right_expression);
        changes.move(rExprStart, currentFile->startOf(pattern->rparen_token), insertPos);
        changes.insert(insertPos, QLatin1String(")"));
852

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
856 857 858 859 860 861
        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();
862 863 864
    }

private:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
865 866 867
    IfStatementAST *pattern;
    BinaryExpressionAST *condition;
};
868

869 870
} // anonymous namespace

Nikolai Kosjar's avatar
Nikolai Kosjar committed
871 872 873 874
void SplitIfStatement::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
{
    IfStatementAST *pattern = 0;
    const QList<AST *> &path = interface->path();
875

Nikolai Kosjar's avatar
Nikolai Kosjar committed
876 877 878 879 880 881
    int index = path.size() - 1;
    for (; index != -1; --index) {
        AST *node = path.at(index);
        if (IfStatementAST *stmt = node->asIfStatement()) {
            pattern = stmt;
            break;
882
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
883
    }
884

Nikolai Kosjar's avatar
Nikolai Kosjar committed
885 886
    if (! pattern || ! pattern->statement)
        return;
887

Nikolai Kosjar's avatar
Nikolai Kosjar committed
888 889 890 891 892 893
    unsigned splitKind = 0;
    for (++index; index < path.size(); ++index) {
        AST *node = path.at(index);
        BinaryExpressionAST *condition = node->asBinaryExpression();
        if (! condition)
            return;
894

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
897 898 899 900 901 902 903 904 905 906
        // 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;
907 908
        }

Nikolai Kosjar's avatar
Nikolai Kosjar committed
909 910 911 912
        if (interface->isCursorOn(condition->binary_op_token)) {
            result.append(QuickFixOperation::Ptr(
                new SplitIfStatementOp(interface, index, pattern, condition)));
            return;
913
        }
Nikolai Kosjar's avatar
Nikolai Kosjar committed
914 915
    }
}
916

917 918 919 920
/* 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,
921
                                          const CppRefactoringFilePtr &file, Type *type,
922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943
                                          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;
944
        }
945
    }