cppcodegen_test.cpp 22.5 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
Bill King's avatar
Bill King committed
2
**
3 4
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
Bill King's avatar
Bill King committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
Bill King's avatar
Bill King committed
7
**
hjk's avatar
hjk committed
8 9 10 11
** 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.
Bill King's avatar
Bill King committed
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.
Bill King's avatar
Bill King committed
23
**
hjk's avatar
hjk committed
24
****************************************************************************/
Bill King's avatar
Bill King committed
25

26
#include "cpptoolsplugin.h"
27 28
#include "cpptoolstestcase.h"
#include "insertionpointlocator.h"
29

30
#include <utils/fileutils.h>
31
#include <utils/qtcassert.h>
32 33

#include <QtTest>
hjk's avatar
hjk committed
34
#include <QDebug>
35
#include <QDir>
36

37 38 39 40
/*!
    Tests for various parts of the code generation. Well, okay, currently it only
    tests the InsertionPointLocator.
 */
41
using namespace CPlusPlus;
42
using namespace CppTools;
43
using namespace CppTools::Internal;
44

45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
namespace {

Document::Ptr createDocument(const QString filePath, const QByteArray text,
                             unsigned expectedGlobalSymbolCount)
{
    Document::Ptr document = Document::create(filePath);
    document->setUtf8Source(text);
    document->check();
    QTC_ASSERT(document->diagnosticMessages().isEmpty(), return Document::Ptr());
    QTC_ASSERT(document->globalSymbolCount() == expectedGlobalSymbolCount, return Document::Ptr());

    return document;
}

Document::Ptr createDocumentAndFile(Tests::TemporaryDir *temporaryDir,
                                    const QByteArray relativeFilePath,
                                    const QByteArray text,
                                    unsigned expectedGlobalSymbolCount)
{
    QTC_ASSERT(temporaryDir, return Document::Ptr());
    const QString absoluteFilePath = temporaryDir->createFile(relativeFilePath, text);
    QTC_ASSERT(!absoluteFilePath.isEmpty(), return Document::Ptr());

    return createDocument(absoluteFilePath, text, expectedGlobalSymbolCount);
}

} // anonymous namespace

73 74 75
/*!
    Should insert at line 3, column 1, with "public:\n" as prefix and without suffix.
 */
76
void CppToolsPlugin::test_codegen_public_in_empty_class()
77 78 79 80
{
    const QByteArray src = "\n"
            "class Foo\n" // line 1
            "{\n"
81
            "};\n"
82
            "\n";
83 84
    Document::Ptr doc = createDocument(QLatin1String("public_in_empty_class"), src, 1U);
    QVERIFY(doc);
85

86 87 88 89 90 91 92
    Class *foo = doc->globalSymbolAt(0)->asClass();
    QVERIFY(foo);
    QCOMPARE(foo->line(), 1U);
    QCOMPARE(foo->column(), 7U);

    Snapshot snapshot;
    snapshot.insert(doc);
93
    CppRefactoringChanges changes(snapshot);
94
    InsertionPointLocator find(changes);
95
    InsertionLocation loc = find.methodDeclarationInClass(
96 97
                doc->fileName(),
                foo,
98
                InsertionPointLocator::Public);
99
    QVERIFY(loc.isValid());
100
    QCOMPARE(loc.prefix(), QLatin1String("public:\n"));
101
    QVERIFY(loc.suffix().isEmpty());
102
    QCOMPARE(loc.line(), 3U);
103 104 105
    QCOMPARE(loc.column(), 1U);
}

106 107 108
/*!
    Should insert at line 3, column 1, without prefix and without suffix.
 */
109
void CppToolsPlugin::test_codegen_public_in_nonempty_class()
110 111 112 113
{
    const QByteArray src = "\n"
            "class Foo\n" // line 1
            "{\n"
114 115
            "public:\n"   // line 3
            "};\n"        // line 4
116
            "\n";
117 118
    Document::Ptr doc = createDocument(QLatin1String("public_in_nonempty_class"), src, 1U);
    QVERIFY(doc);
119

120 121 122 123
    Class *foo = doc->globalSymbolAt(0)->asClass();
    QVERIFY(foo);
    QCOMPARE(foo->line(), 1U);
    QCOMPARE(foo->column(), 7U);
124

125 126
    Snapshot snapshot;
    snapshot.insert(doc);
127
    CppRefactoringChanges changes(snapshot);
128
    InsertionPointLocator find(changes);
129
    InsertionLocation loc = find.methodDeclarationInClass(
130 131
                doc->fileName(),
                foo,
132
                InsertionPointLocator::Public);
133
    QVERIFY(loc.isValid());
134
    QVERIFY(loc.prefix().isEmpty());
135
    QVERIFY(loc.suffix().isEmpty());
136
    QCOMPARE(loc.line(), 4U);
137 138 139
    QCOMPARE(loc.column(), 1U);
}

140 141 142
/*!
    Should insert at line 3, column 1, with "public:\n" as prefix and "\n suffix.
 */
143
void CppToolsPlugin::test_codegen_public_before_protected()
144 145 146 147 148 149 150
{
    const QByteArray src = "\n"
            "class Foo\n"  // line 1
            "{\n"
            "protected:\n" // line 3
            "};\n"
            "\n";
151 152
    Document::Ptr doc = createDocument(QLatin1String("public_before_protected"), src, 1U);
    QVERIFY(doc);
153

154 155 156 157
    Class *foo = doc->globalSymbolAt(0)->asClass();
    QVERIFY(foo);
    QCOMPARE(foo->line(), 1U);
    QCOMPARE(foo->column(), 7U);
158

159 160
    Snapshot snapshot;
    snapshot.insert(doc);
161
    CppRefactoringChanges changes(snapshot);
162
    InsertionPointLocator find(changes);
163
    InsertionLocation loc = find.methodDeclarationInClass(
164 165
                doc->fileName(),
                foo,
166
                InsertionPointLocator::Public);
167 168 169 170 171 172 173
    QVERIFY(loc.isValid());
    QCOMPARE(loc.prefix(), QLatin1String("public:\n"));
    QCOMPARE(loc.suffix(), QLatin1String("\n"));
    QCOMPARE(loc.column(), 1U);
    QCOMPARE(loc.line(), 3U);
}

174 175 176 177
/*!
    Should insert at line 4, column 1, with "private:\n" as prefix and without
    suffix.
 */
178
void CppToolsPlugin::test_codegen_private_after_protected()
179 180 181 182 183 184 185
{
    const QByteArray src = "\n"
            "class Foo\n"  // line 1
            "{\n"
            "protected:\n" // line 3
            "};\n"
            "\n";
186 187
    Document::Ptr doc = createDocument(QLatin1String("private_after_protected"), src, 1U);
    QVERIFY(doc);
188

189 190 191 192
    Class *foo = doc->globalSymbolAt(0)->asClass();
    QVERIFY(foo);
    QCOMPARE(foo->line(), 1U);
    QCOMPARE(foo->column(), 7U);
193

194 195
    Snapshot snapshot;
    snapshot.insert(doc);
196
    CppRefactoringChanges changes(snapshot);
197
    InsertionPointLocator find(changes);
198
    InsertionLocation loc = find.methodDeclarationInClass(
199 200
                doc->fileName(),
                foo,
201
                InsertionPointLocator::Private);
202 203 204 205 206 207 208
    QVERIFY(loc.isValid());
    QCOMPARE(loc.prefix(), QLatin1String("private:\n"));
    QVERIFY(loc.suffix().isEmpty());
    QCOMPARE(loc.column(), 1U);
    QCOMPARE(loc.line(), 4U);
}

209 210 211 212
/*!
    Should insert at line 4, column 1, with "protected:\n" as prefix and without
    suffix.
 */
213
void CppToolsPlugin::test_codegen_protected_in_nonempty_class()
214 215 216 217 218 219 220
{
    const QByteArray src = "\n"
            "class Foo\n" // line 1
            "{\n"
            "public:\n"   // line 3
            "};\n"        // line 4
            "\n";
221 222
    Document::Ptr doc = createDocument(QLatin1String("protected_in_nonempty_class"), src, 1U);
    QVERIFY(doc);
223

224 225 226 227
    Class *foo = doc->globalSymbolAt(0)->asClass();
    QVERIFY(foo);
    QCOMPARE(foo->line(), 1U);
    QCOMPARE(foo->column(), 7U);
228

229 230
    Snapshot snapshot;
    snapshot.insert(doc);
231
    CppRefactoringChanges changes(snapshot);
232
    InsertionPointLocator find(changes);
233
    InsertionLocation loc = find.methodDeclarationInClass(
234 235
                doc->fileName(),
                foo,
236
                InsertionPointLocator::Protected);
237 238 239 240 241 242 243
    QVERIFY(loc.isValid());
    QCOMPARE(loc.prefix(), QLatin1String("protected:\n"));
    QVERIFY(loc.suffix().isEmpty());
    QCOMPARE(loc.column(), 1U);
    QCOMPARE(loc.line(), 4U);
}

244 245 246
/*!
    Should insert at line 4, column 1, with "protected\n" as prefix and "\n" suffix.
 */
247
void CppToolsPlugin::test_codegen_protected_between_public_and_private()
248 249 250 251 252 253 254 255
{
    const QByteArray src = "\n"
            "class Foo\n" // line 1
            "{\n"
            "public:\n"   // line 3
            "private:\n"  // line 4
            "};\n"        // line 5
            "\n";
256 257
    Document::Ptr doc = createDocument(QLatin1String("protected_betwee_public_and_private"), src, 1U);
    QVERIFY(doc);
258

259 260 261 262
    Class *foo = doc->globalSymbolAt(0)->asClass();
    QVERIFY(foo);
    QCOMPARE(foo->line(), 1U);
    QCOMPARE(foo->column(), 7U);
263

264 265
    Snapshot snapshot;
    snapshot.insert(doc);
266
    CppRefactoringChanges changes(snapshot);
267
    InsertionPointLocator find(changes);
268
    InsertionLocation loc = find.methodDeclarationInClass(
269 270
                doc->fileName(),
                foo,
271
                InsertionPointLocator::Protected);
272 273 274 275 276 277 278
    QVERIFY(loc.isValid());
    QCOMPARE(loc.prefix(), QLatin1String("protected:\n"));
    QCOMPARE(loc.suffix(), QLatin1String("\n"));
    QCOMPARE(loc.column(), 1U);
    QCOMPARE(loc.line(), 4U);
}

279 280 281 282 283 284 285
/*!
    Should insert at line 18, column 1, with "private slots:\n" as prefix and "\n"
    as suffix.

    This is the typical Qt Designer case, with test-input like what the integration
    generates.
 */
286
void CppToolsPlugin::test_codegen_qtdesigner_integration()
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
{
    const QByteArray src = "/**** Some long (C)opyright notice ****/\n"
            "#ifndef MAINWINDOW_H\n"
            "#define MAINWINDOW_H\n"
            "\n"
            "#include <QMainWindow>\n"
            "\n"
            "namespace Ui {\n"
            "    class MainWindow;\n"
            "}\n"
            "\n"
            "class MainWindow : public QMainWindow\n" // line 10
            "{\n"
            "    Q_OBJECT\n"
            "\n"
            "public:\n" // line 14
            "    explicit MainWindow(QWidget *parent = 0);\n"
            "    ~MainWindow();\n"
            "\n"
            "private:\n" // line 18
            "    Ui::MainWindow *ui;\n"
            "};\n"
            "\n"
            "#endif // MAINWINDOW_H\n";

312 313
    Document::Ptr doc = createDocument(QLatin1String("qtdesigner_integration"), src, 2U);
    QVERIFY(doc);
314

315 316 317 318
    Class *foo = doc->globalSymbolAt(1)->asClass();
    QVERIFY(foo);
    QCOMPARE(foo->line(), 10U);
    QCOMPARE(foo->column(), 7U);
319

320 321
    Snapshot snapshot;
    snapshot.insert(doc);
322
    CppRefactoringChanges changes(snapshot);
323
    InsertionPointLocator find(changes);
324
    InsertionLocation loc = find.methodDeclarationInClass(
325 326
                doc->fileName(),
                foo,
327
                InsertionPointLocator::PrivateSlot);
328 329 330 331 332 333 334
    QVERIFY(loc.isValid());
    QCOMPARE(loc.prefix(), QLatin1String("private slots:\n"));
    QCOMPARE(loc.suffix(), QLatin1String("\n"));
    QCOMPARE(loc.line(), 18U);
    QCOMPARE(loc.column(), 1U);
}

335
void CppToolsPlugin::test_codegen_definition_empty_class()
336
{
337 338 339 340
    Tests::TemporaryDir temporaryDir;
    QVERIFY(temporaryDir.isValid());

    const QByteArray headerText = "\n"
341 342 343 344 345
            "class Foo\n"  // line 1
            "{\n"
            "void foo();\n" // line 3
            "};\n"
            "\n";
346 347
    Document::Ptr headerDocument = createDocumentAndFile(&temporaryDir, "file.h", headerText, 1U);
    QVERIFY(headerDocument);
348

349
    const QByteArray sourceText = "\n"
350 351
            "int x;\n"  // line 1
            "\n";
352 353
    Document::Ptr sourceDocument = createDocumentAndFile(&temporaryDir, "file.cpp", sourceText, 1U);
    QVERIFY(sourceDocument);
354 355

    Snapshot snapshot;
356 357
    snapshot.insert(headerDocument);
    snapshot.insert(sourceDocument);
358

359
    Class *foo = headerDocument->globalSymbolAt(0)->asClass();
360 361 362 363 364 365 366 367 368 369 370 371 372 373
    QVERIFY(foo);
    QCOMPARE(foo->line(), 1U);
    QCOMPARE(foo->column(), 7U);
    QCOMPARE(foo->memberCount(), 1U);
    Declaration *decl = foo->memberAt(0)->asDeclaration();
    QVERIFY(decl);
    QCOMPARE(decl->line(), 3U);
    QCOMPARE(decl->column(), 6U);

    CppRefactoringChanges changes(snapshot);
    InsertionPointLocator find(changes);
    QList<InsertionLocation> locList = find.methodDefinition(decl);
    QVERIFY(locList.size() == 1);
    InsertionLocation loc = locList.first();
374
    QCOMPARE(loc.fileName(), sourceDocument->fileName());
375 376
    QCOMPARE(loc.prefix(), QLatin1String("\n\n"));
    QCOMPARE(loc.suffix(), QString());
Leandro Melo's avatar
Leandro Melo committed
377 378
    QCOMPARE(loc.line(), 3U);
    QCOMPARE(loc.column(), 1U);
379 380
}

381
void CppToolsPlugin::test_codegen_definition_first_member()
382
{
383 384 385 386
    Tests::TemporaryDir temporaryDir;
    QVERIFY(temporaryDir.isValid());

    const QByteArray headerText = "\n"
387 388 389 390 391 392
            "class Foo\n"  // line 1
            "{\n"
            "void foo();\n" // line 3
            "void bar();\n" // line 4
            "};\n"
            "\n";
393 394
    Document::Ptr headerDocument = createDocumentAndFile(&temporaryDir, "file.h", headerText, 1U);
    QVERIFY(headerDocument);
395

396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411
    const QByteArray sourceText = QString::fromLatin1(
            "\n"
            "#include \"%1/file.h\"\n" // line 1
            "int x;\n"
            "\n"
            "void Foo::bar()\n" // line 4
            "{\n"
            "\n"
            "}\n"
            "\n"
            "int y;\n").arg(temporaryDir.path()).toLatin1();
    Document::Ptr sourceDocument = createDocumentAndFile(&temporaryDir, "file.cpp", sourceText, 3U);
    QVERIFY(sourceDocument);
    sourceDocument->addIncludeFile(Document::Include(QLatin1String("file.h"),
                                                     headerDocument->fileName(), 1,
                                                     Client::IncludeLocal));
412 413

    Snapshot snapshot;
414 415
    snapshot.insert(headerDocument);
    snapshot.insert(sourceDocument);
416

417
    Class *foo = headerDocument->globalSymbolAt(0)->asClass();
418 419 420 421 422 423 424 425 426 427 428 429 430 431
    QVERIFY(foo);
    QCOMPARE(foo->line(), 1U);
    QCOMPARE(foo->column(), 7U);
    QCOMPARE(foo->memberCount(), 2U);
    Declaration *decl = foo->memberAt(0)->asDeclaration();
    QVERIFY(decl);
    QCOMPARE(decl->line(), 3U);
    QCOMPARE(decl->column(), 6U);

    CppRefactoringChanges changes(snapshot);
    InsertionPointLocator find(changes);
    QList<InsertionLocation> locList = find.methodDefinition(decl);
    QVERIFY(locList.size() == 1);
    InsertionLocation loc = locList.first();
432
    QCOMPARE(loc.fileName(), sourceDocument->fileName());
433 434 435 436 437 438
    QCOMPARE(loc.line(), 4U);
    QCOMPARE(loc.column(), 1U);
    QCOMPARE(loc.suffix(), QLatin1String("\n\n"));
    QCOMPARE(loc.prefix(), QString());
}

439
void CppToolsPlugin::test_codegen_definition_last_member()
440
{
441 442 443 444
    Tests::TemporaryDir temporaryDir;
    QVERIFY(temporaryDir.isValid());

    const QByteArray headerText = "\n"
445 446 447 448 449 450
            "class Foo\n"  // line 1
            "{\n"
            "void foo();\n" // line 3
            "void bar();\n" // line 4
            "};\n"
            "\n";
451 452
    Document::Ptr headerDocument = createDocumentAndFile(&temporaryDir, "file.h", headerText, 1U);
    QVERIFY(headerDocument);
453

454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470
    const QByteArray sourceText = QString::fromLatin1(
            "\n"
            "#include \"%1/file.h\"\n" // line 1
            "int x;\n"
            "\n"
            "void Foo::foo()\n" // line 4
            "{\n"
            "\n"
            "}\n" // line 7
            "\n"
            "int y;\n").arg(temporaryDir.path()).toLatin1();

    Document::Ptr sourceDocument = createDocumentAndFile(&temporaryDir, "file.cpp", sourceText, 3U);
    QVERIFY(sourceDocument);
    sourceDocument->addIncludeFile(Document::Include(QLatin1String("file.h"),
                                                     headerDocument->fileName(), 1,
                                                     Client::IncludeLocal));
471 472

    Snapshot snapshot;
473 474
    snapshot.insert(headerDocument);
    snapshot.insert(sourceDocument);
475

476
    Class *foo = headerDocument->globalSymbolAt(0)->asClass();
477 478 479 480 481 482 483 484 485 486 487 488 489 490
    QVERIFY(foo);
    QCOMPARE(foo->line(), 1U);
    QCOMPARE(foo->column(), 7U);
    QCOMPARE(foo->memberCount(), 2U);
    Declaration *decl = foo->memberAt(1)->asDeclaration();
    QVERIFY(decl);
    QCOMPARE(decl->line(), 4U);
    QCOMPARE(decl->column(), 6U);

    CppRefactoringChanges changes(snapshot);
    InsertionPointLocator find(changes);
    QList<InsertionLocation> locList = find.methodDefinition(decl);
    QVERIFY(locList.size() == 1);
    InsertionLocation loc = locList.first();
491
    QCOMPARE(loc.fileName(), sourceDocument->fileName());
492 493 494 495 496 497
    QCOMPARE(loc.line(), 7U);
    QCOMPARE(loc.column(), 2U);
    QCOMPARE(loc.prefix(), QLatin1String("\n\n"));
    QCOMPARE(loc.suffix(), QString());
}

498
void CppToolsPlugin::test_codegen_definition_middle_member()
499
{
500 501 502 503
    Tests::TemporaryDir temporaryDir;
    QVERIFY(temporaryDir.isValid());

    const QByteArray headerText = "\n"
504 505 506 507 508 509 510 511
            "class Foo\n"  // line 1
            "{\n"
            "void foo();\n" // line 3
            "void bar();\n" // line 4
            "void car();\n" // line 5
            "};\n"
            "\n";

512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536
    Document::Ptr headerDocument = createDocumentAndFile(&temporaryDir, "file.h", headerText, 1U);
    QVERIFY(headerDocument);

    const QByteArray sourceText = QString::fromLatin1(
            "\n"
            "#include \"%1/file.h\"\n" // line 1
            "int x;\n"
            "\n"
            "void Foo::foo()\n" // line 4
            "{\n"
            "\n"
            "}\n" // line 7
            "\n"
            "void Foo::car()\n" // line 9
            "{\n"
            "\n"
            "}\n"
            "\n"
            "int y;\n").arg(QDir::tempPath()).toLatin1();

    Document::Ptr sourceDocument = createDocumentAndFile(&temporaryDir, "file.cpp", sourceText, 4U);
    QVERIFY(sourceDocument);
    sourceDocument->addIncludeFile(Document::Include(QLatin1String("file.h"),
                                                     headerDocument->fileName(), 1,
                                                     Client::IncludeLocal));
537 538

    Snapshot snapshot;
539 540
    snapshot.insert(headerDocument);
    snapshot.insert(sourceDocument);
541

542
    Class *foo = headerDocument->globalSymbolAt(0)->asClass();
543 544 545 546 547 548 549 550 551 552 553 554 555 556
    QVERIFY(foo);
    QCOMPARE(foo->line(), 1U);
    QCOMPARE(foo->column(), 7U);
    QCOMPARE(foo->memberCount(), 3U);
    Declaration *decl = foo->memberAt(1)->asDeclaration();
    QVERIFY(decl);
    QCOMPARE(decl->line(), 4U);
    QCOMPARE(decl->column(), 6U);

    CppRefactoringChanges changes(snapshot);
    InsertionPointLocator find(changes);
    QList<InsertionLocation> locList = find.methodDefinition(decl);
    QVERIFY(locList.size() == 1);
    InsertionLocation loc = locList.first();
557
    QCOMPARE(loc.fileName(), sourceDocument->fileName());
558 559 560 561 562
    QCOMPARE(loc.line(), 7U);
    QCOMPARE(loc.column(), 2U);
    QCOMPARE(loc.prefix(), QLatin1String("\n\n"));
    QCOMPARE(loc.suffix(), QString());
}
563 564 565

void CppToolsPlugin::test_codegen_definition_middle_member_surrounded_by_undefined()
{
566 567 568 569
    Tests::TemporaryDir temporaryDir;
    QVERIFY(temporaryDir.isValid());

    const QByteArray headerText = "\n"
570 571 572 573 574 575 576 577
            "class Foo\n"  // line 1
            "{\n"
            "void foo();\n" // line 3
            "void bar();\n" // line 4
            "void baz();\n" // line 5
            "void car();\n" // line 6
            "};\n"
            "\n";
578 579
    Document::Ptr headerDocument = createDocumentAndFile(&temporaryDir, "file.h", headerText, 1U);
    QVERIFY(headerDocument);
580

581
    const QByteArray sourceText = QString::fromLatin1(
582 583 584 585 586 587 588 589 590
            "\n"
            "#include \"%1/file.h\"\n" // line 1
            "int x;\n"
            "\n"
            "void Foo::car()\n" // line 4
            "{\n"
            "\n"
            "}\n"
            "\n"
591 592 593 594 595 596
            "int y;\n").arg(temporaryDir.path()).toLatin1();
    Document::Ptr sourceDocument = createDocumentAndFile(&temporaryDir, "file.cpp", sourceText, 3U);
    QVERIFY(sourceDocument);
    sourceDocument->addIncludeFile(Document::Include(QLatin1String("file.h"),
                                                     headerDocument->fileName(), 1,
                                                     Client::IncludeLocal));
597 598

    Snapshot snapshot;
599 600
    snapshot.insert(headerDocument);
    snapshot.insert(sourceDocument);
601

602
    Class *foo = headerDocument->globalSymbolAt(0)->asClass();
603 604 605 606 607 608 609 610 611 612 613 614 615 616
    QVERIFY(foo);
    QCOMPARE(foo->line(), 1U);
    QCOMPARE(foo->column(), 7U);
    QCOMPARE(foo->memberCount(), 4U);
    Declaration *decl = foo->memberAt(1)->asDeclaration();
    QVERIFY(decl);
    QCOMPARE(decl->line(), 4U);
    QCOMPARE(decl->column(), 6U);

    CppRefactoringChanges changes(snapshot);
    InsertionPointLocator find(changes);
    QList<InsertionLocation> locList = find.methodDefinition(decl);
    QVERIFY(locList.size() == 1);
    InsertionLocation loc = locList.first();
617
    QCOMPARE(loc.fileName(), sourceDocument->fileName());
618 619 620 621 622 623 624 625
    QCOMPARE(loc.line(), 4U);
    QCOMPARE(loc.column(), 1U);
    QCOMPARE(loc.prefix(), QString());
    QCOMPARE(loc.suffix(), QLatin1String("\n\n"));
}

void CppToolsPlugin::test_codegen_definition_member_specific_file()
{
626 627 628 629
    Tests::TemporaryDir temporaryDir;
    QVERIFY(temporaryDir.isValid());

    const QByteArray headerText = "\n"
630 631 632 633 634 635 636 637 638 639 640
            "class Foo\n"  // line 1
            "{\n"
            "void foo();\n" // line 3
            "void bar();\n" // line 4
            "void baz();\n" // line 5
            "};\n"
            "\n"
            "void Foo::bar()\n"
            "{\n"
            "\n"
            "}\n";
641 642
    Document::Ptr headerDocument = createDocumentAndFile(&temporaryDir, "file.h", headerText, 2U);
    QVERIFY(headerDocument);
643

644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659
    const QByteArray sourceText = QString::fromLatin1(
            "\n"
            "#include \"%1/file.h\"\n" // line 1
            "int x;\n"
            "\n"
            "void Foo::foo()\n" // line 4
            "{\n"
            "\n"
            "}\n" // line 7
            "\n"
            "int y;\n").arg(temporaryDir.path()).toLatin1();
    Document::Ptr sourceDocument = createDocumentAndFile(&temporaryDir, "file.cpp", sourceText, 3U);
    QVERIFY(sourceDocument);
    sourceDocument->addIncludeFile(Document::Include(QLatin1String("file.h"),
                                                     headerDocument->fileName(), 1,
                                                     Client::IncludeLocal));
660 661

    Snapshot snapshot;
662 663
    snapshot.insert(headerDocument);
    snapshot.insert(sourceDocument);
664

665
    Class *foo = headerDocument->globalSymbolAt(0)->asClass();
666 667 668 669 670 671 672 673 674 675 676
    QVERIFY(foo);
    QCOMPARE(foo->line(), 1U);
    QCOMPARE(foo->column(), 7U);
    QCOMPARE(foo->memberCount(), 3U);
    Declaration *decl = foo->memberAt(2)->asDeclaration();
    QVERIFY(decl);
    QCOMPARE(decl->line(), 5U);
    QCOMPARE(decl->column(), 6U);

    CppRefactoringChanges changes(snapshot);
    InsertionPointLocator find(changes);
677
    QList<InsertionLocation> locList = find.methodDefinition(decl, true, sourceDocument->fileName());
678 679
    QVERIFY(locList.size() == 1);
    InsertionLocation loc = locList.first();
680
    QCOMPARE(loc.fileName(), sourceDocument->fileName());
681 682 683 684 685
    QCOMPARE(loc.line(), 7U);
    QCOMPARE(loc.column(), 2U);
    QCOMPARE(loc.prefix(), QLatin1String("\n\n"));
    QCOMPARE(loc.suffix(), QString());
}