followsymbol_switchmethoddecldef_test.cpp 55.3 KB
Newer Older
1 2
/****************************************************************************
**
3 4
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
5 6 7 8 9 10 11
**
** This file is part of Qt Creator.
**
** 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
12 13 14
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
15
**
16 17 18 19 20 21 22
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 24 25 26
**
****************************************************************************/

#include "cppeditorplugin.h"
27
#include "cppeditortestcase.h"
28
#include "cppelementevaluator.h"
29
#include "cppfollowsymbolundercursor.h"
30
#include "cppvirtualfunctionassistprovider.h"
31
#include "cppvirtualfunctionproposalitem.h"
32

33 34
#include <cpptools/cpptoolstestcase.h>

35
#include <texteditor/codeassist/genericproposalmodel.h>
36 37 38
#include <texteditor/codeassist/iassistprocessor.h>
#include <texteditor/codeassist/iassistproposal.h>

39
#include <coreplugin/editormanager/editormanager.h>
40
#include <coreplugin/idocument.h>
41

42 43 44 45 46 47
#include <utils/fileutils.h>

#include <QDebug>
#include <QDir>
#include <QtTest>

48 49 50 51 52 53 54 55 56 57 58 59 60
//
// The following "non-latin1" code points are used in the tests:
//
//   U+00FC  - 2 code units in UTF8, 1 in UTF16 - LATIN SMALL LETTER U WITH DIAERESIS
//   U+4E8C  - 3 code units in UTF8, 1 in UTF16 - CJK UNIFIED IDEOGRAPH-4E8C
//   U+10302 - 4 code units in UTF8, 2 in UTF16 - OLD ITALIC LETTER KE
//

#define UNICODE_U00FC "\xc3\xbc"
#define UNICODE_U4E8C "\xe4\xba\x8c"
#define UNICODE_U10302 "\xf0\x90\x8c\x82"
#define TEST_UNICODE_IDENTIFIER UNICODE_U00FC UNICODE_U4E8C UNICODE_U10302

61
/*!
62
    Tests for Follow Symbol Under Cursor and Switch Between Function Declaration/Definition
63 64 65 66 67 68

    Section numbers refer to

        Working Draft, Standard for Programming Language C++
        Document Number: N3242=11-0012

69
    You can find potential test code for Follow Symbol Under Cursor on the bottom of this file.
70
 */
71

72 73 74 75 76
using namespace CPlusPlus;
using namespace CppTools;
using namespace TextEditor;
using namespace Core;

77 78 79 80 81
class OverrideItem {
public:
    OverrideItem() : line(0) {}
    OverrideItem(const QString &text, int line = 0) : text(text), line(line) {}
    bool isValid() { return line != 0; }
82 83 84 85
    QByteArray toByteArray() const
    {
        return "OverrideItem(" + text.toLatin1() + ", " + QByteArray::number(line) + ')';
    }
86 87 88 89 90 91 92 93 94 95 96 97

    QString text;
    int line;
};
typedef QList<OverrideItem> OverrideItemList;
Q_DECLARE_METATYPE(OverrideItem)

inline bool operator==(const OverrideItem &lhs, const OverrideItem &rhs)
{
    return lhs.text == rhs.text && lhs.line == rhs.line;
}

98
QT_BEGIN_NAMESPACE
99 100 101
namespace QTest {
template<> char *toString(const OverrideItem &data)
{
102
    return qstrdup(data.toByteArray().data());
103 104
}
}
105 106 107 108 109 110

QDebug &operator<<(QDebug &d, const OverrideItem &data)
{
    d << data.toByteArray();
    return d;
}
111
QT_END_NAMESPACE
112

Orgad Shaneh's avatar
Orgad Shaneh committed
113 114
typedef QByteArray _;

115 116 117
namespace CppEditor {
namespace Internal {

118 119 120 121
/// A fake virtual functions assist provider that runs processor->perform() already in configure()
class VirtualFunctionTestAssistProvider : public VirtualFunctionAssistProvider
{
public:
122
    VirtualFunctionTestAssistProvider(CppEditorWidget *editorWidget)
123 124 125 126 127 128
        : m_editorWidget(editorWidget)
    {}

    // Invoke the processor already here to calculate the proposals. Return false in order to
    // indicate that configure failed, so the actual code assist invocation leading to a pop-up
    // will not happen.
129
    bool configure(const VirtualFunctionAssistProvider::Parameters &params) override
130
    {
131
        VirtualFunctionAssistProvider::configure(params);
132

133
        const QScopedPointer<IAssistProcessor> processor(createProcessor());
134
        AssistInterface *assistInterface
135
                = m_editorWidget->createAssistInterface(FollowSymbol, ExplicitlyInvoked);
136 137 138 139 140

        using CppTools::Tests::IAssistProposalScopedPointer;
        const IAssistProposalScopedPointer immediateProposal(
            processor->immediateProposal(assistInterface));
        const IAssistProposalScopedPointer finalProposal(processor->perform(assistInterface));
141

142 143
        VirtualFunctionAssistProvider::clearParams();

144 145
        m_immediateItems = itemList(immediateProposal.d->model());
        m_finalItems = itemList(finalProposal.d->model());
146 147 148 149

        return false;
    }

150
    static OverrideItemList itemList(IAssistProposalModel *imodel)
151
    {
152
        OverrideItemList result;
153
        GenericProposalModel *model = dynamic_cast<GenericProposalModel *>(imodel);
154
        if (!model)
155
            return result;
156 157 158 159

        // Mimic relevant GenericProposalWidget::showProposal() calls
        model->removeDuplicates();
        model->reset();
160 161 162 163
        if (model->isSortable(QString()))
            model->sort(QString());

        for (int i = 0, size = model->size(); i < size; ++i) {
164 165 166
            VirtualFunctionProposalItem *item
                = dynamic_cast<VirtualFunctionProposalItem *>(model->proposalItem(i));

167
            const QString text = model->text(i);
168 169 170 171
            const int line = item->link().targetLine;
//            Uncomment for updating/generating reference data:
//            qDebug("<< OverrideItem(QLatin1String(\"%s\"), %d)", qPrintable(text), line);
            result << OverrideItem(text, line);
172 173
        }

174
        return result;
175 176 177
    }

public:
178 179
    OverrideItemList m_immediateItems;
    OverrideItemList m_finalItems;
180 181

private:
182
    CppEditorWidget *m_editorWidget;
183 184
};

185 186 187 188 189 190 191 192 193 194
class TestDocument;
typedef QSharedPointer<TestDocument> TestDocumentPtr;

/**
 * Represents a test document.
 *
 * A TestDocument's source can contain special characters:
 *   - a '@' character denotes the initial text cursor position
 *   - a '$' character denotes the target text cursor position
 */
195
class TestDocument : public Tests::TestDocument
196 197
{
public:
198
    TestDocument(const QByteArray &source, const QByteArray &fileName)
199
        : Tests::TestDocument(fileName, source)
200
        , m_targetCursorPosition(m_source.indexOf(QLatin1Char('$')))
201
    {
202 203 204 205 206 207 208 209
        if (m_cursorPosition != -1 || m_targetCursorPosition != -1)
            QVERIFY(m_cursorPosition != m_targetCursorPosition);

        if (m_cursorPosition > m_targetCursorPosition) {
            m_source.remove(m_cursorPosition, 1);
            if (m_targetCursorPosition != -1) {
                m_source.remove(m_targetCursorPosition, 1);
                --m_cursorPosition;
210
            }
211
        } else {
212 213 214 215
            m_source.remove(m_targetCursorPosition, 1);
            if (m_cursorPosition != -1) {
                m_source.remove(m_cursorPosition, 1);
                --m_targetCursorPosition;
216
            }
217 218 219
        }
    }

220
    static TestDocumentPtr create(const QByteArray &source, const QByteArray &fileName)
221
    {
222
        return TestDocumentPtr(new TestDocument(source, fileName));
223 224
    }

225
    bool hasTargetCursorMarker() const { return m_targetCursorPosition != -1; }
226

227 228
public:
    int m_targetCursorPosition;
229 230
};

231 232 233 234 235
QList<TestDocumentPtr> singleDocument(const QByteArray &source)
{
    return QList<TestDocumentPtr>() << TestDocument::create(source, "file.cpp");
}

236 237
/**
 * Encapsulates the whole process of setting up several editors, positioning the cursor,
238
 * executing Follow Symbol Under Cursor or Switch Between Function Declaration/Definition
239
 * and checking the result.
240
 */
241
class F2TestCase : public Tests::TestCase
242
{
243 244
public:
    enum CppEditorAction {
245 246
        FollowSymbolUnderCursorAction,
        SwitchBetweenMethodDeclarationDefinitionAction
247
    };
248

249
    F2TestCase(CppEditorAction action,
250
               const QList<TestDocumentPtr> &testFiles,
251
               const OverrideItemList &expectedVirtualFunctionProposal = OverrideItemList());
252 253

private:
254 255
    static TestDocumentPtr testFileWithInitialCursorMarker(const QList<TestDocumentPtr> &testFiles);
    static TestDocumentPtr testFileWithTargetCursorMarker(const QList<TestDocumentPtr> &testFiles);
256 257 258 259 260 261
};

/// Creates a test case with multiple test files.
/// Exactly one test document must be provided that contains '@', the initial position marker.
/// Exactly one test document must be provided that contains '$', the target position marker.
/// It can be the same document.
262
F2TestCase::F2TestCase(CppEditorAction action,
263
                       const QList<TestDocumentPtr> &testFiles,
264
                       const OverrideItemList &expectedVirtualFunctionProposal)
265
{
266
    QVERIFY(succeededSoFar());
267 268

    // Check if there are initial and target position markers
269 270
    TestDocumentPtr initialTestFile = testFileWithInitialCursorMarker(testFiles);
    QVERIFY2(initialTestFile,
271
        "No test file with initial cursor marker is provided.");
272 273
    TestDocumentPtr targetTestFile = testFileWithTargetCursorMarker(testFiles);
    QVERIFY2(targetTestFile,
274 275 276
        "No test file with target cursor marker is provided.");

    // Write files to disk
277 278 279 280 281
    CppTools::Tests::TemporaryDir temporaryDir;
    QVERIFY(temporaryDir.isValid());
    foreach (TestDocumentPtr testFile, testFiles) {
        QVERIFY(testFile->baseDirectory().isEmpty());
        testFile->setBaseDirectory(temporaryDir.path());
282
        QVERIFY(testFile->writeToDisk());
283
    }
284 285

    // Update Code Model
286
    QSet<QString> filePaths;
287
    foreach (const TestDocumentPtr &testFile, testFiles)
288
        filePaths << testFile->filePath();
289
    QVERIFY(parseFiles(filePaths));
290 291

    // Open Files
292
    foreach (TestDocumentPtr testFile, testFiles) {
293 294 295
        QVERIFY(openCppEditor(testFile->filePath(), &testFile->m_editor,
                              &testFile->m_editorWidget));
        closeEditorAtEndOfTestCase(testFile->m_editor);
296 297

        // Wait until the indexer processed the just opened file.
298
        // The file is "Full Checked" since it is in the working copy now,
299
        // that is the function bodies are processed.
300
        forever {
301
            const Document::Ptr document = waitForFileInGlobalSnapshot(testFile->filePath());
302
            QVERIFY(document);
303 304
            if (document->checkMode() == Document::FullCheck) {
                QVERIFY(document->diagnosticMessages().isEmpty());
305
                break;
306
            }
307 308 309
        }

        // Rehighlight
310
        waitForRehighlightedSemanticDocument(testFile->m_editorWidget);
311 312
    }

313
    // Activate editor of initial test file
314
    EditorManager::activateEditor(initialTestFile->m_editor);
315

316
    initialTestFile->m_editor->setCursorPosition(initialTestFile->m_cursorPosition);
317 318
//    qDebug() << "Initial line:" << initialTestFile->editor->currentLine();
//    qDebug() << "Initial column:" << initialTestFile->editor->currentColumn() - 1;
319

320 321
    OverrideItemList immediateVirtualSymbolResults;
    OverrideItemList finalVirtualSymbolResults;
322

323
    // Trigger the action
324
    switch (action) {
325
    case FollowSymbolUnderCursorAction: {
326
        CppEditorWidget *widget = initialTestFile->m_editorWidget;
327 328 329 330 331 332 333
        FollowSymbolUnderCursor *delegate = widget->followSymbolUnderCursorDelegate();
        VirtualFunctionAssistProvider *original = delegate->virtualFunctionAssistProvider();

        // Set test provider, run and get results
        QScopedPointer<VirtualFunctionTestAssistProvider> testProvider(
            new VirtualFunctionTestAssistProvider(widget));
        delegate->setVirtualFunctionAssistProvider(testProvider.data());
334
        initialTestFile->m_editorWidget->openLinkUnderCursor();
335 336 337 338 339
        immediateVirtualSymbolResults = testProvider->m_immediateItems;
        finalVirtualSymbolResults = testProvider->m_finalItems;

        // Restore original test provider
        delegate->setVirtualFunctionAssistProvider(original);
340
        break;
341 342
    }
    case SwitchBetweenMethodDeclarationDefinitionAction:
343 344 345 346 347 348
        CppEditorPlugin::instance()->switchDeclarationDefinition();
        break;
    default:
        QFAIL("Unknown test action");
        break;
    }
349 350 351 352

    QCoreApplication::processEvents();

    // Compare
353
    IEditor *currentEditor = EditorManager::currentEditor();
354 355 356
    BaseTextEditor *currentTextEditor = dynamic_cast<BaseTextEditor*>(currentEditor);
    QVERIFY(currentTextEditor);

357
    QCOMPARE(currentTextEditor->document()->filePath().toString(), targetTestFile->filePath());
358
    int expectedLine, expectedColumn;
359
    currentTextEditor->convertPosition(targetTestFile->m_targetCursorPosition,
360 361 362
                                       &expectedLine, &expectedColumn);
//    qDebug() << "Expected line:" << expectedLine;
//    qDebug() << "Expected column:" << expectedColumn;
363

Orgad Shaneh's avatar
Orgad Shaneh committed
364
    QEXPECT_FAIL("globalVarFromEnum", "Contributor works on a fix.", Abort);
365
    QEXPECT_FAIL("matchFunctionSignature_Follow_5", "foo(int) resolved as CallAST", Abort);
366 367
    QCOMPARE(currentTextEditor->currentLine(), expectedLine);
    QCOMPARE(currentTextEditor->currentColumn() - 1, expectedColumn);
368 369 370

//    qDebug() << immediateVirtualSymbolResults;
//    qDebug() << finalVirtualSymbolResults;
371
    OverrideItemList expectedImmediate;
372 373
    if (!expectedVirtualFunctionProposal.isEmpty()) {
        expectedImmediate << expectedVirtualFunctionProposal.first();
374 375 376
        expectedImmediate << OverrideItem(QLatin1String("...searching overrides"));
    }
    QCOMPARE(immediateVirtualSymbolResults, expectedImmediate);
377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
    QCOMPARE(finalVirtualSymbolResults, expectedVirtualFunctionProposal);
}

TestDocumentPtr F2TestCase::testFileWithInitialCursorMarker(const QList<TestDocumentPtr> &testFiles)
{
    foreach (const TestDocumentPtr testFile, testFiles) {
        if (testFile->hasCursorMarker())
            return testFile;
    }
    return TestDocumentPtr();
}

TestDocumentPtr F2TestCase::testFileWithTargetCursorMarker(const QList<TestDocumentPtr> &testFiles)
{
    foreach (const TestDocumentPtr testFile, testFiles) {
        if (testFile->hasTargetCursorMarker())
            return testFile;
    }
    return TestDocumentPtr();
396 397
}

398 399
} // namespace Internal
} // namespace CppEditor
400

401 402 403 404
Q_DECLARE_METATYPE(QList<CppEditor::Internal::TestDocumentPtr>)

namespace CppEditor {
namespace Internal {
405

Orgad Shaneh's avatar
Orgad Shaneh committed
406
void CppEditorPlugin::test_SwitchMethodDeclarationDefinition_data()
407
{
Orgad Shaneh's avatar
Orgad Shaneh committed
408 409
    QTest::addColumn<QByteArray>("header");
    QTest::addColumn<QByteArray>("source");
410

Orgad Shaneh's avatar
Orgad Shaneh committed
411
    QTest::newRow("fromFunctionDeclarationSymbol") << _(
412 413 414 415 416 417
        "class C\n"
        "{\n"
        "public:\n"
        "    C();\n"
        "    int @function();\n"  // Line 5
        "};\n"
Orgad Shaneh's avatar
Orgad Shaneh committed
418
        ) << _(
419 420 421 422 423 424 425 426 427 428
        "#include \"file.h\"\n"
        "\n"
        "C::C()\n"
        "{\n"
        "}\n"                   // Line 5
        "\n"
        "int C::$function()\n"
        "{\n"
        "    return 1 + 1;\n"
        "}\n"                   // Line 10
Orgad Shaneh's avatar
Orgad Shaneh committed
429
    );
430

Orgad Shaneh's avatar
Orgad Shaneh committed
431
    QTest::newRow("fromFunctionDefinitionSymbol") << _(
432 433 434 435 436 437
        "class C\n"
        "{\n"
        "public:\n"
        "    C();\n"
        "    int $function();\n"
        "};\n"
Orgad Shaneh's avatar
Orgad Shaneh committed
438
        ) << _(
439 440 441 442 443 444 445 446 447 448
        "#include \"file.h\"\n"
        "\n"
        "C::C()\n"
        "{\n"
        "}\n"
        "\n"
        "int C::@function()\n"
        "{\n"
        "    return 1 + 1;\n"
        "}\n"
Orgad Shaneh's avatar
Orgad Shaneh committed
449
    );
450

Orgad Shaneh's avatar
Orgad Shaneh committed
451
    QTest::newRow("fromFunctionBody") << _(
452 453 454 455 456 457
        "class C\n"
        "{\n"
        "public:\n"
        "    C();\n"
        "    int $function();\n"
        "};\n"
Orgad Shaneh's avatar
Orgad Shaneh committed
458
        ) << _(
459 460 461 462 463 464 465 466 467 468
        "#include \"file.h\"\n"
        "\n"
        "C::C()\n"
        "{\n"
        "}\n"                   // Line 5
        "\n"
        "int C::function()\n"
        "{\n"
        "    return @1 + 1;\n"
        "}\n"                   // Line 10
Orgad Shaneh's avatar
Orgad Shaneh committed
469
    );
470

Orgad Shaneh's avatar
Orgad Shaneh committed
471
    QTest::newRow("fromReturnType") << _(
472 473 474 475 476 477
        "class C\n"
        "{\n"
        "public:\n"
        "    C();\n"
        "    int $function();\n"
        "};\n"
Orgad Shaneh's avatar
Orgad Shaneh committed
478
        ) << _(
479 480 481 482 483 484 485 486 487 488
        "#include \"file.h\"\n"
        "\n"
        "C::C()\n"
        "{\n"
        "}\n"                   // Line 5
        "\n"
        "@int C::function()\n"
        "{\n"
        "    return 1 + 1;\n"
        "}\n"                   // Line 10
Orgad Shaneh's avatar
Orgad Shaneh committed
489
    );
490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533

    QTest::newRow("matchFunctionSignature_Def_1") << _(
        "class Foo {\n"
        "    void @foo(int);\n"
        "    void foo();\n"
        "};\n"
        ) << _(
        "#include \"file.h\"\n"
        "void Foo::$foo(int) {}\n"
    );

    QTest::newRow("matchFunctionSignature_Def_2") << _(
        "class Foo {\n"
        "    void $foo(int);\n"
        "    void foo();\n"
        "};\n"
        ) << _(
        "#include \"file.h\"\n"
        "void Foo::@foo(int) {}\n"
    );

    QTest::newRow("matchFunctionSignature_Def_3") << _(
        "class Foo {\n"
        "    void foo(int);\n"
        "    void @$foo() {}\n"
        "};\n"
        ) << _(
        "#include \"file.h\"\n"
        "void Foo::foo(int) {}\n"
    );

    QTest::newRow("matchFunctionSignature_Def_4") << _(
        "class Foo {\n"
        "    void foo(int);\n"
        "    void @$foo();\n"
        "};\n"
    ) << _();

    QTest::newRow("matchFunctionSignature_Def_5") << _(
        "class Foo {\n"
        "    void @$foo(int);\n"
        "    void foo();\n"
        "};\n"
    ) << _();
534 535

    QTest::newRow("unicodeIdentifier") << _(
536 537
        "class Foo { void $" TEST_UNICODE_IDENTIFIER "(); };\n"
        "void Foo::@" TEST_UNICODE_IDENTIFIER "() {}\n"
538
    ) << _();
Orgad Shaneh's avatar
Orgad Shaneh committed
539 540 541 542 543 544 545
}

void CppEditorPlugin::test_SwitchMethodDeclarationDefinition()
{
    QFETCH(QByteArray, header);
    QFETCH(QByteArray, source);

546 547 548
    const QList<TestDocumentPtr> testFiles = QList<TestDocumentPtr>()
        << TestDocument::create(header, "file.h")
        << TestDocument::create(source, "file.cpp");
549

550
    F2TestCase(F2TestCase::SwitchBetweenMethodDeclarationDefinitionAction, testFiles);
551 552
}

Orgad Shaneh's avatar
Orgad Shaneh committed
553
void CppEditorPlugin::test_FollowSymbolUnderCursor_data()
554
{
Orgad Shaneh's avatar
Orgad Shaneh committed
555 556 557 558
    QTest::addColumn<QByteArray>("source");

    /// Check ...
    QTest::newRow("globalVarFromFunction") << _(
559 560 561 562 563
        "int $j;\n"
        "int main()\n"
        "{\n"
        "    @j = 2;\n"
        "}\n"           // Line 5
Orgad Shaneh's avatar
Orgad Shaneh committed
564
    );
565 566

    // 3.3.10 Name hiding (par 3.), from text
Orgad Shaneh's avatar
Orgad Shaneh committed
567
    QTest::newRow("funLocalVarHidesClassMember") << _(
568 569 570 571 572 573 574 575
        "struct C {\n"
        "    void f()\n"
        "    {\n"
        "        int $member; // hides C::member\n"
        "        ++@member;\n"                       // Line 5
        "    }\n"
        "    int member;\n"
        "};\n"
Orgad Shaneh's avatar
Orgad Shaneh committed
576
    );
577 578

    // 3.3.10 Name hiding (par 4.), from text
Orgad Shaneh's avatar
Orgad Shaneh committed
579
    QTest::newRow("funLocalVarHidesNamespaceMemberIntroducedByUsingDirective") << _(
580 581 582 583 584 585 586 587 588 589
        "namespace N {\n"
        "    int i;\n"
        "}\n"
        "\n"
        "int main()\n"                       // Line 5
        "{\n"
        "    using namespace N;\n"
        "    int $i;\n"
        "    ++i@; // refers to local i;\n"
        "}\n"                                // Line 10
Orgad Shaneh's avatar
Orgad Shaneh committed
590
    );
591 592 593

    // 3.3.3 Block scope (par. 4), from text
    // Same for if, while, switch
Orgad Shaneh's avatar
Orgad Shaneh committed
594
    QTest::newRow("loopLocalVarHidesOuterScopeVariable1") << _(
595 596 597 598 599 600
        "int main()\n"
        "{\n"
        "    int i = 1;\n"
        "    for (int $i = 0; i < 10; ++i) { // 'i' refers to for's i\n"
        "        i = @i; // same\n"                                       // Line 5
        "    }\n"
Orgad Shaneh's avatar
Orgad Shaneh committed
601 602
        "}\n"
    );
603 604 605

    // 3.3.3 Block scope (par. 4), from text
    // Same for if, while, switch
Orgad Shaneh's avatar
Orgad Shaneh committed
606
    QTest::newRow("loopLocalVarHidesOuterScopeVariable2") << _(
607 608 609 610 611 612
        "int main()\n"
        "{\n"
        "    int i = 1;\n"
        "    for (int $i = 0; @i < 10; ++i) { // 'i' refers to for's i\n"
        "        i = i; // same\n"                                         // Line 5
        "    }\n"
Orgad Shaneh's avatar
Orgad Shaneh committed
613 614
        "}\n"
    );
615 616

    // 3.3.7 Class scope, part of the example
Orgad Shaneh's avatar
Orgad Shaneh committed
617
    QTest::newRow("subsequentDefinedClassMember") << _(
618 619 620 621
        "class X {\n"
        "    int f() { return @i; } // i refers to class's i\n"
        "    int $i;\n"
        "};\n"
Orgad Shaneh's avatar
Orgad Shaneh committed
622
    );
623 624 625

    // 3.3.7 Class scope, part of the example
    // Variable name hides type name.
Orgad Shaneh's avatar
Orgad Shaneh committed
626
    QTest::newRow("classMemberHidesOuterTypeDef") << _(
627 628 629 630 631
        "typedef int c;\n"
        "class X {\n"
        "    int f() { return @c; } // c refers to class' c\n"
        "    int $c; // hides typedef name\n"
        "};\n"                                                 // Line 5
Orgad Shaneh's avatar
Orgad Shaneh committed
632
    );
633 634

    // 3.3.2 Point of declaration (par. 1), copy-paste
Orgad Shaneh's avatar
Orgad Shaneh committed
635
    QTest::newRow("globalVarFromEnum") << _(
636 637 638 639 640
        "const int $x = 12;\n"
        "int main()\n"
        "{\n"
        "    enum { x = @x }; // x refers to global x\n"
        "}\n"                                             // Line 5
Orgad Shaneh's avatar
Orgad Shaneh committed
641
    );
642 643

    // 3.3.2 Point of declaration
Orgad Shaneh's avatar
Orgad Shaneh committed
644
    QTest::newRow("selfInitialization") << _(
645 646 647 648 649
        "int x = 12;\n"
        "int main()\n"
        "{\n"
        "   int $x = @x; // Second x refers to local x\n"
        "}\n"                                              // Line 5
Orgad Shaneh's avatar
Orgad Shaneh committed
650
    );
651 652

    // 3.3.2 Point of declaration (par. 3), from text
Orgad Shaneh's avatar
Orgad Shaneh committed
653
    QTest::newRow("pointerToClassInClassDefinition") << _(
654 655 656
        "class $Foo {\n"
        "    @Foo *p; // Refers to above Foo\n"
        "};\n"
Orgad Shaneh's avatar
Orgad Shaneh committed
657
    );
658 659

    // 3.3.2 Point of declaration (par. 5), copy-paste
Orgad Shaneh's avatar
Orgad Shaneh committed
660
    QTest::newRow("previouslyDefinedMemberFromArrayDefinition") << _(
661 662 663 664
        "struct X {\n"
        "    enum E { $z = 16 };\n"
        "    int b[X::@z]; // z refers to defined z\n"
        "};\n"
Orgad Shaneh's avatar
Orgad Shaneh committed
665
    );
666 667

    // 3.3.7 Class scope (par. 2), from text
Orgad Shaneh's avatar
Orgad Shaneh committed
668
    QTest::newRow("outerStaticMemberVariableFromInsideSubclass") << _(
669 670 671 672 673 674 675 676 677 678 679 680
        "struct C\n"
        "{\n"
        "   struct I\n"
        "   {\n"
        "       void f()\n"                            // Line 5
        "       {\n"
        "           int i = @c; // refers to C's c\n"
        "       }\n"
        "   };\n"
        "\n"                                           // Line 10
        "   static int $c;\n"
        "};\n"
Orgad Shaneh's avatar
Orgad Shaneh committed
681
    );
682 683

    // 3.3.7 Class scope (par. 1), part of point 5
Orgad Shaneh's avatar
Orgad Shaneh committed
684
    QTest::newRow("memberVariableFollowingDotOperator") << _(
685 686 687 688 689 690 691 692 693 694
        "struct C\n"
        "{\n"
        "    int $member;\n"
        "};\n"
        "\n"                 // Line 5
        "int main()\n"
        "{\n"
        "    C c;\n"
        "    c.@member++;\n"
        "}\n"                // Line 10
Orgad Shaneh's avatar
Orgad Shaneh committed
695
    );
696 697

    // 3.3.7 Class scope (par. 1), part of point 5
Orgad Shaneh's avatar
Orgad Shaneh committed
698
    QTest::newRow("memberVariableFollowingArrowOperator") << _(
699 700 701 702 703 704 705 706 707 708
        "struct C\n"
        "{\n"
        "    int $member;\n"
        "};\n"
        "\n"                    // Line 5
        "int main()\n"
        "{\n"
        "    C* c;\n"
        "    c->@member++;\n"
        "}\n"                   // Line 10
Orgad Shaneh's avatar
Orgad Shaneh committed
709
    );
710 711

    // 3.3.7 Class scope (par. 1), part of point 5
Orgad Shaneh's avatar
Orgad Shaneh committed
712
    QTest::newRow("staticMemberVariableFollowingScopeOperator") << _(
713 714 715 716 717 718 719 720 721
        "struct C\n"
        "{\n"
        "    static int $member;\n"
        "};\n"
        "\n"                        // Line 5
        "int main()\n"
        "{\n"
        "    C::@member++;\n"
        "}\n"
Orgad Shaneh's avatar
Orgad Shaneh committed
722
    );
723 724

    // 3.3.7 Class scope (par. 2), from text
Orgad Shaneh's avatar
Orgad Shaneh committed
725
    QTest::newRow("staticMemberVariableFollowingDotOperator") << _(
726 727 728 729 730 731 732 733 734 735
        "struct C\n"
        "{\n"
        "    static int $member;\n"
        "};\n"
        "\n"                        // Line 5
        "int main()\n"
        "{\n"
        "    C c;\n"
        "    c.@member;\n"
        "}\n"                       // Line 10
Orgad Shaneh's avatar
Orgad Shaneh committed
736
    );
737 738 739


    // 3.3.7 Class scope (par. 2), from text
Orgad Shaneh's avatar
Orgad Shaneh committed
740
    QTest::newRow("staticMemberVariableFollowingArrowOperator") << _(
741 742 743 744 745 746 747 748 749 750
        "struct C\n"
        "{\n"
        "    static int $member;\n"
        "};\n"
        "\n"                         // Line 5
        "int main()\n"
        "{\n"
        "    C *c;\n"
        "    c->@member++;\n"
        "}\n"                        // Line 10
Orgad Shaneh's avatar
Orgad Shaneh committed
751
    );
752 753

    // 3.3.8 Enumeration scope
Orgad Shaneh's avatar
Orgad Shaneh committed
754
    QTest::newRow("previouslyDefinedEnumValueFromInsideEnum") << _(
755 756 757 758
        "enum {\n"
        "    $i = 0,\n"
        "    j = @i // refers to i above\n"
        "};\n"
Orgad Shaneh's avatar
Orgad Shaneh committed
759
    );
760 761

    // 3.3.8 Enumeration scope
Orgad Shaneh's avatar
Orgad Shaneh committed
762
    QTest::newRow("nsMemberHidesNsMemberIntroducedByUsingDirective") << _(
763
        "namespace A {\n"
764
        "    char x;\n"
765 766 767
        "}\n"
        "\n"
        "namespace B {\n"                               // Line 5
768 769
        "    using namespace A;\n"
        "    int $x; // hides A::x\n"
770 771 772 773 774 775
        "}\n"
        "\n"
        "int main()\n"                                  // Line 10
        "{\n"
        "    B::@x++; // refers to B's X, not A::x\n"
        "}\n"
Orgad Shaneh's avatar
Orgad Shaneh committed
776
    );
777 778 779

    // 3.3.10 Name hiding, from text
    // www.stroustrup.com/bs_faq2.html#overloadderived
Orgad Shaneh's avatar
Orgad Shaneh committed
780
    QTest::newRow("baseClassFunctionIntroducedByUsingDeclaration") << _(
781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796
        "struct B {\n"
        "    int $f(int) {}\n"
        "};\n"
        "\n"
        "class D : public B {\n"                              // Line 5
        "public:\n"
        "    using B::f; // make every f from B available\n"
        "    double f(double) {}\n"
        "};\n"
        "\n"                                                  // Line 10
        "int main()\n"
        "{\n"
        "    D* pd = new D;\n"
        "    pd->@f(2); // refers to B::f\n"
        "    pd->f(2.3); // refers to D::f\n"                 // Line 15
        "}\n"
Orgad Shaneh's avatar
Orgad Shaneh committed
797
    );
798 799 800

    // 3.3.10 Name hiding, from text
    // www.stroustrup.com/bs_faq2.html#overloadderived
Orgad Shaneh's avatar
Orgad Shaneh committed
801
    QTest::newRow("funWithSameNameAsBaseClassFunIntroducedByUsingDeclaration") << _(
802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817
        "struct B {\n"
        "    int f(int) {}\n"
        "};\n"
        "\n"
        "class D : public B {\n"                              // Line 5
        "public:\n"
        "    using B::f; // make every f from B available\n"
        "    double $f(double) {}\n"
        "};\n"
        "\n"                                                  // Line 10
        "int main()\n"
        "{\n"
        "    D* pd = new D;\n"
        "    pd->f(2); // refers to B::f\n"
        "    pd->@f(2.3); // refers to D::f\n"                // Line 15
        "}\n"
Orgad Shaneh's avatar
Orgad Shaneh committed
818
    );
819 820

    // 3.3.10 Name hiding (par 2.), from text
821 822
    // A class name (9.1) or enumeration name (7.2) can be hidden by the name of a variable,
    // data member, function, or enumerator declared in the same scope.
Orgad Shaneh's avatar
Orgad Shaneh committed
823
    QTest::newRow("funLocalVarHidesOuterClass") << _(
824 825 826 827 828 829 830
        "struct C {};\n"
        "\n"
        "int main()\n"
        "{\n"
        "    int $C; // hides type C\n"  // Line 5
        "    ++@C;\n"
        "}\n"
Orgad Shaneh's avatar
Orgad Shaneh committed
831
    );
832

Orgad Shaneh's avatar
Orgad Shaneh committed
833
    QTest::newRow("classConstructor") << _(
834 835 836 837 838 839 840 841 842 843
        "class Foo {\n"
        "    F@oo();"
        "    ~Foo();"
        "};\n\n"
        "Foo::$Foo()\n"
        "{\n"
        "}\n\n"
        "Foo::~Foo()\n"
        "{\n"
        "}\n"
Orgad Shaneh's avatar
Orgad Shaneh committed
844
    );
845

Orgad Shaneh's avatar
Orgad Shaneh committed
846
    QTest::newRow("classDestructor") << _(
847 848 849 850 851 852 853 854 855 856
        "class Foo {\n"
        "    Foo();"
        "    ~@Foo();"
        "};\n\n"
        "Foo::Foo()\n"
        "{\n"
        "}\n\n"
        "Foo::~$Foo()\n"
        "{\n"
        "}\n"
Orgad Shaneh's avatar
Orgad Shaneh committed
857 858
    );

859
    QTest::newRow("skipForwardDeclarationBasic") << _(
860 861 862
        "class $Foo {};\n"
        "class Foo;\n"
        "@Foo foo;\n"
863 864
    );

865
    QTest::newRow("skipForwardDeclarationTemplates") << _(
866 867 868
        "template <class E> class $Container {};\n"
        "template <class E> class Container;\n"
        "@Container<int> container;\n"
869 870
    );

Orgad Shaneh's avatar
Orgad Shaneh committed
871
    QTest::newRow("using_QTCREATORBUG7903_globalNamespace") << _(
872 873 874 875 876 877 878 879
        "namespace NS {\n"
        "class Foo {};\n"
        "}\n"
        "using NS::$Foo;\n"
        "void fun()\n"
        "{\n"
        "    @Foo foo;\n"
        "}\n"
Orgad Shaneh's avatar
Orgad Shaneh committed
880 881 882
    );

    QTest::newRow("using_QTCREATORBUG7903_namespace") << _(
883 884 885 886 887 888 889 890 891 892
        "namespace NS {\n"
        "class Foo {};\n"
        "}\n"
        "namespace NS1 {\n"
        "void fun()\n"
        "{\n"
        "    using NS::$Foo;\n"
        "    @Foo foo;\n"
        "}\n"
        "}\n"
Orgad Shaneh's avatar
Orgad Shaneh committed
893
    );
894

Orgad Shaneh's avatar
Orgad Shaneh committed
895
    QTest::newRow("using_QTCREATORBUG7903_insideFunction") << _(
896 897 898 899 900 901 902 903
        "namespace NS {\n"
        "class Foo {};\n"
        "}\n"
        "void fun()\n"
        "{\n"
        "    using NS::$Foo;\n"
        "    @Foo foo;\n"
        "}\n"
Orgad Shaneh's avatar
Orgad Shaneh committed
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

    QTest::newRow("matchFunctionSignature_Follow_1") << _(
        "class Foo {\n"
        "    void @foo(int);\n"
        "    void foo();\n"
        "};\n"
        "void Foo::$foo(int) {}\n"
    );

    QTest::newRow("matchFunctionSignature_Follow_2") << _(
        "class Foo {\n"
        "    void $foo(int);\n"
        "    void foo();\n"
        "};\n"
        "void Foo::@foo(int) {}\n"
    );

    QTest::newRow("matchFunctionSignature_Follow_3") << _(
        "class Foo {\n"
        "    void foo(int);\n"
        "    void @$foo() {}\n"
        "};\n"
        "void Foo::foo(int) {}\n"
    );

    QTest::newRow("matchFunctionSignature_Follow_4") << _(
        "class Foo {\n"
        "    void foo(int);\n"
        "    void @$foo();\n"
        "};\n"
    );

    QTest::newRow("matchFunctionSignature_Follow_5") << _(
        "class Foo {\n"
        "    void @$foo(int);\n"
        "    void foo();\n"
        "};\n"
    );
943

944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971
    QTest::newRow("matchFunctionSignature_Follow_6") << _(
        "class Foo {\n"
        "    void $foo(int);\n"
        "};\n"
        "void Foo::@foo(const volatile int) {}\n"
    );

    QTest::newRow("matchFunctionSignature_Follow_7") << _(
        "class Foo {\n"
        "    void $foo(const volatile int);\n"
        "};\n"
        "void Foo::@foo(int) {}\n"
    );

    QTest::newRow("matchFunctionSignature_Follow_8") << _(
        "class Foo {\n"
        "    void @$foo(int *);\n"
        "};\n"
        "void Foo::foo(const int *) {}\n"
    );

    QTest::newRow("matchFunctionSignature_Follow_9") << _(
        "class Foo {\n"
        "    void @$foo(int&);\n"
        "};\n"
        "void Foo::foo(const int&) {}\n"
    );

972 973 974 975 976 977 978 979 980 981
    QTest::newRow("infiniteLoopLocalTypedef_QTCREATORBUG-11999") << _(
        "template<class MyTree>\n"
        "class TreeConstIterator\n"
        "{\n"
        "    typedef TreeConstIterator<MyTree> MyIter;\n"
        "    void f() { return this->@$g(); }\n"
        "};\n"
        "\n"
        "void h() { typedef TreeConstIterator<MyBase> const_iterator; }\n"
    );
982 983

    QTest::newRow("unicodeIdentifier") << _(
984 985
        "class Foo { void $" TEST_UNICODE_IDENTIFIER "(); };\n"
        "void Foo::@" TEST_UNICODE_IDENTIFIER "() {}\n"
986
    );
987 988 989 990 991

    QTest::newRow("trailingReturnType") << _(
        "struct $Foo {};\n"
        "auto foo() -> @Foo {}\n"
    );
992 993 994 995 996

    QTest::newRow("template_alias") << _(
        "template<class $T>\n"
        "using Foo = Bar<@T>;\n"
    );
Orgad Shaneh's avatar
Orgad Shaneh committed
997 998 999 1000 1001
}

void CppEditorPlugin::test_FollowSymbolUnderCursor()
{
    QFETCH(QByteArray, source);
1002
    F2TestCase(F2TestCase::FollowSymbolUnderCursorAction, singleDocument(source));
1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069
}

void CppEditorPlugin::test_FollowSymbolUnderCursor_followCall_data()
{
    QTest::addColumn<QByteArray>("variableDeclaration"); // without semicolon, can be ""
    QTest::addColumn<QByteArray>("callArgument");
    QTest::addColumn<QByteArray>("expectedSignature"); // you might need to add a function
                                                       // declaration with such a signature

    QTest::newRow("intLiteral-to-int")
            << _("")
            << _("5")
            << _("int");
    QTest::newRow("charLiteral-to-const-char-ptr")
            << _("")
            << _("\"hoo\"")
            << _("const char *");
    QTest::newRow("charLiteral-to-int")
            << _("")
            << _("'a'")
            << _("char");

    QTest::newRow("charPtr-to-constCharPtr")
            << _("char *var = \"var\"")
            << _("var")
            << _("const char *");
    QTest::newRow("charPtr-to-constCharPtr")
            << _("char *var = \"var\"")
            << _("var")
            << _("const char *");
    QTest::newRow("constCharPtr-to-constCharPtr")
            << _("const char *var = \"var\"")
            << _("var")
            << _("const char *");

    QTest::newRow("Bar-to-constBarRef")
            << _("Bar var")
            << _("var")
            << _("const Bar &");
}

void CppEditorPlugin::test_FollowSymbolUnderCursor_followCall()
{
    QFETCH(QByteArray, variableDeclaration);
    QFETCH(QByteArray, callArgument);
    QFETCH(QByteArray, expectedSignature);

    const QByteArray templateSource =
        "class Bar {};\n"
        "void fun(int);\n"
        "void fun(const char *);\n"
        "void fun(const Bar &);\n"
        "void fun(char);\n"
        "void fun(double);\n"
        "\n"
        "void t()\n"
        "{\n"
        "   " + variableDeclaration + ";\n"
        "   @fun(" + callArgument + ");\n"
        "}\n";

    const QByteArray matchText = " fun(" + expectedSignature + ")";
    const QByteArray replaceText = " $fun(" + expectedSignature + ")";
    QByteArray source = templateSource;
    source.replace(matchText, replaceText);
    QVERIFY(source != templateSource);
    F2TestCase(F2TestCase::FollowSymbolUnderCursorAction, singleDocument(source));
Joerg Bornemann's avatar
Joerg Bornemann committed
1070 1071
}

1072 1073 1074 1075 1076 1077
void CppEditorPlugin::test_FollowSymbolUnderCursor_multipleDocuments_data()
{
    QTest::addColumn<QList<TestDocumentPtr> >("documents");

    QTest::newRow("skipForwardDeclarationBasic") << (QList<TestDocumentPtr>()
        << TestDocument::create("class $Foo {};\n",
1078
                                "defined.h")
1079 1080
        << TestDocument::create("class Foo;\n"
                                "@Foo foo;\n",
1081
                                "forwardDeclaredAndUsed.h")
1082
    );
1083 1084 1085

    QTest::newRow("skipForwardDeclarationTemplates") << (QList<TestDocumentPtr>()
        << TestDocument::create("template <class E> class $Container {};\n",
1086
                                "defined.h")
1087 1088
        << TestDocument::create("template <class E> class Container;\n"
                                "@Container<int> container;\n",
1089
                                "forwardDeclaredAndUsed.h")
1090
    );
1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101

    QTest::newRow("matchFunctionSignature") << (QList<TestDocumentPtr>()
        << TestDocument::create("class Foo {\n"
                                "    void @foo(int);\n"
                                "    void foo() {}\n"
                                "};\n",
                                "foo.h")
        << TestDocument::create("#include \"foo.h\"\n"
                                "void Foo::$foo(int) {}\n",
                                "foo.cpp")
    );
Nikolai Kosjar's avatar
Nikolai Kosjar committed
1102 1103 1104 1105 1106 1107 1108 1109 1110 1111

    QTest::newRow("matchFunctionSignature2") << (QList<TestDocumentPtr>()
        << TestDocument::create("namespace N { class C; }\n"
                                "bool *@fun(N::C *) const;\n",
                                "foo.h")
        << TestDocument::create("#include \"foo.h\