cppmodelmanager.cpp 31.1 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
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
con's avatar
con committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
con's avatar
con committed
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
****************************************************************************/
hjk's avatar
hjk committed
29

con's avatar
con committed
30
#include "cppmodelmanager.h"
31

32
#include "abstracteditorsupport.h"
33
#include "builtinindexingsupport.h"
34
#include "cppfindreferences.h"
35
#include "cpphighlightingsupport.h"
36
#include "cppindexingsupport.h"
37
#include "cppmodelmanagersupportinternal.h"
38 39
#include "cpppreprocessor.h"
#include "cpptoolsconstants.h"
40
#include "cpptoolseditorsupport.h"
con's avatar
con committed
41

42 43
#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h>
44 45
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/session.h>
46
#include <extensionsystem/pluginmanager.h>
hjk's avatar
hjk committed
47
#include <utils/qtcassert.h>
con's avatar
con committed
48

49 50 51 52
#include <QCoreApplication>
#include <QDebug>
#include <QMutexLocker>
#include <QTextBlock>
53
#include <QTimer>
54

55 56
#if defined(QTCREATOR_WITH_DUMP_AST) && defined(Q_CC_GNU)
#define WITH_AST_DUMP
57 58
#include <iostream>
#include <sstream>
59
#endif
hjk's avatar
hjk committed
60

61
namespace CppTools {
62

63
uint qHash(const ProjectPart &p)
64
{
65
    uint h = qHash(p.defines) ^ p.cVersion ^ p.cxxVersion ^ p.cxxExtensions ^ p.qtVersion;
66 67 68 69 70 71 72 73 74

    foreach (const QString &i, p.includePaths)
        h ^= qHash(i);

    foreach (const QString &f, p.frameworkPaths)
        h ^= qHash(f);

    return h;
}
75

76 77
bool operator==(const ProjectPart &p1,
                const ProjectPart &p2)
78 79 80
{
    if (p1.defines != p2.defines)
        return false;
81 82 83 84 85
    if (p1.cVersion != p2.cVersion)
        return false;
    if (p1.cxxVersion != p2.cxxVersion)
        return false;
    if (p1.cxxExtensions != p2.cxxExtensions)
86
        return false;
87
    if (p1.qtVersion!= p2.qtVersion)
88 89 90 91 92
        return false;
    if (p1.includePaths != p2.includePaths)
        return false;
    return p1.frameworkPaths == p2.frameworkPaths;
}
93

94
} // namespace CppTools
95

96 97
using namespace CppTools;
using namespace CppTools::Internal;
con's avatar
con committed
98 99
using namespace CPlusPlus;

100
#ifdef QTCREATOR_WITH_DUMP_AST
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 126 127 128 129 130 131 132 133 134 135 136 137 138 139

#include <cxxabi.h>

class DumpAST: protected ASTVisitor
{
public:
    int depth;

    DumpAST(Control *control)
        : ASTVisitor(control), depth(0)
    { }

    void operator()(AST *ast)
    { accept(ast); }

protected:
    virtual bool preVisit(AST *ast)
    {
        std::ostringstream s;
        PrettyPrinter pp(control(), s);
        pp(ast);
        QString code = QString::fromStdString(s.str());
        code.replace('\n', ' ');
        code.replace(QRegExp("\\s+"), " ");

        const char *name = abi::__cxa_demangle(typeid(*ast).name(), 0, 0, 0) + 11;

        QByteArray ind(depth, ' ');
        ind += name;

        printf("%-40s %s\n", ind.constData(), qPrintable(code));
        ++depth;
        return true;
    }

    virtual void postVisit(AST *)
    { --depth; }
};

Roberto Raggi's avatar
Roberto Raggi committed
140
#endif // QTCREATOR_WITH_DUMP_AST
141

con's avatar
con committed
142 143
static const char pp_configuration[] =
    "# 1 \"<configuration>\"\n"
144
    "#define Q_CREATOR_RUN 1\n"
con's avatar
con committed
145 146 147 148 149 150
    "#define __cplusplus 1\n"
    "#define __extension__\n"
    "#define __context__\n"
    "#define __range__\n"
    "#define   restrict\n"
    "#define __restrict\n"
151
    "#define __restrict__\n"
con's avatar
con committed
152

153 154 155 156
    "#define __complex__\n"
    "#define __imag__\n"
    "#define __real__\n"

157 158
    "#define __builtin_va_arg(a,b) ((b)0)\n"

con's avatar
con committed
159 160
    // ### add macros for win32
    "#define __cdecl\n"
161
    "#define __stdcall\n"
con's avatar
con committed
162 163 164 165 166
    "#define QT_WA(x) x\n"
    "#define CALLBACK\n"
    "#define STDMETHODCALLTYPE\n"
    "#define __RPC_FAR\n"
    "#define __declspec(a)\n"
167 168 169
    "#define STDMETHOD(method) virtual HRESULT STDMETHODCALLTYPE method\n"
    "#define __try try\n"
    "#define __except catch\n"
170 171 172
    "#define __finally\n"
    "#define __inline inline\n"
    "#define __forceinline inline\n";
con's avatar
con committed
173

174
QStringList CppModelManager::timeStampModifiedFiles(const QList<Document::Ptr> documentsToCheck)
175 176 177
{
    QStringList sourceFiles;

178
    foreach (const Document::Ptr doc, documentsToCheck) {
179 180
        const QDateTime lastModified = doc->lastModified();

181
        if (!lastModified.isNull()) {
182 183 184 185 186 187 188
            QFileInfo fileInfo(doc->fileName());

            if (fileInfo.exists() && fileInfo.lastModified() != lastModified)
                sourceFiles.append(doc->fileName());
        }
    }

189 190 191 192 193 194 195 196 197 198 199 200
    return sourceFiles;
}

void CppModelManager::updateModifiedSourceFiles()
{
    const Snapshot snapshot = this->snapshot();
    QList<Document::Ptr> documentsToCheck;
    foreach (const Document::Ptr document, snapshot)
        documentsToCheck << document;

    const QStringList filesToUpdate = timeStampModifiedFiles(documentsToCheck);
    updateSourceFiles(filesToUpdate);
201 202
}

con's avatar
con committed
203 204
/*!
    \class CppTools::CppModelManager
205 206 207
    \brief The CppModelManager keeps tracks of the source files the code model is aware of.

    The CppModelManager manages the source files in a Snapshot object.
con's avatar
con committed
208

209 210 211 212
    The snapshot is updated in case e.g.
        * New files are opened/edited (Editor integration)
        * A project manager pushes updated project information (Project integration)
        * Files are garbage collected
con's avatar
con committed
213 214
*/

215 216
QMutex CppModelManager::m_instanceMutex;
CppModelManager *CppModelManager::m_instance = 0;
217 218 219

CppModelManager *CppModelManager::instance()
{
220 221 222 223 224 225 226 227
    if (m_instance)
        return m_instance;

    QMutexLocker locker(&m_instanceMutex);
    if (!m_instance)
        m_instance = new CppModelManager;

    return m_instance;
228 229
}

230
CppModelManager::CppModelManager(QObject *parent)
231
    : CppModelManagerInterface(parent)
232
    , m_indexingSupporter(0)
233
    , m_enableGC(true)
con's avatar
con committed
234
{
Roberto Raggi's avatar
Roberto Raggi committed
235
    m_findReferences = new CppFindReferences(this);
236
    m_indexerEnabled = qgetenv("QTCREATOR_NO_CODE_INDEXER").isNull();
Roberto Raggi's avatar
Roberto Raggi committed
237

238 239
    m_dirty = true;

240 241 242 243
    m_delayedGcTimer = new QTimer(this);
    m_delayedGcTimer->setSingleShot(true);
    connect(m_delayedGcTimer, SIGNAL(timeout()), this, SLOT(GC()));

hjk's avatar
hjk committed
244 245
    QObject *sessionManager = ProjectExplorer::SessionManager::instance();
    connect(sessionManager, SIGNAL(projectAdded(ProjectExplorer::Project*)),
246
            this, SLOT(onProjectAdded(ProjectExplorer::Project*)));
hjk's avatar
hjk committed
247
    connect(sessionManager, SIGNAL(aboutToRemoveProject(ProjectExplorer::Project*)),
Robert Loehning's avatar
Robert Loehning committed
248
            this, SLOT(onAboutToRemoveProject(ProjectExplorer::Project*)));
hjk's avatar
hjk committed
249
    connect(sessionManager, SIGNAL(aboutToLoadSession(QString)),
250
            this, SLOT(onAboutToLoadSession()));
hjk's avatar
hjk committed
251
    connect(sessionManager, SIGNAL(aboutToUnloadSession(QString)),
252
            this, SLOT(onAboutToUnloadSession()));
con's avatar
con committed
253

254 255 256
    connect(Core::ICore::instance(), SIGNAL(coreAboutToClose()),
            this, SLOT(onCoreAboutToClose()));

con's avatar
con committed
257 258
    qRegisterMetaType<CPlusPlus::Document::Ptr>("CPlusPlus::Document::Ptr");

259 260 261
    m_modelManagerSupportFallback.reset(new ModelManagerSupportInternal);
    addModelManagerSupport(m_modelManagerSupportFallback.data());

262
    m_internalIndexingSupport = new BuiltinIndexingSupport;
con's avatar
con committed
263 264 265
}

CppModelManager::~CppModelManager()
266
{
267
    delete m_internalIndexingSupport;
268
}
con's avatar
con committed
269

270
Snapshot CppModelManager::snapshot() const
271
{
272
    QMutexLocker locker(&m_snapshotMutex);
273 274
    return m_snapshot;
}
con's avatar
con committed
275

276 277
Document::Ptr CppModelManager::document(const QString &fileName) const
{
278
    QMutexLocker locker(&m_snapshotMutex);
279 280 281 282 283 284 285 286
    return m_snapshot.document(fileName);
}

/// Replace the document in the snapshot.
///
/// \returns true if successful, false if the new document is out-dated.
bool CppModelManager::replaceDocument(Document::Ptr newDoc)
{
287
    QMutexLocker locker(&m_snapshotMutex);
288 289 290 291 292 293 294 295 296 297

    Document::Ptr previous = m_snapshot.document(newDoc->fileName());
    if (previous && (newDoc->revision() != 0 && newDoc->revision() < previous->revision()))
        // the new document is outdated
        return false;

    m_snapshot.insert(newDoc);
    return true;
}

298 299
void CppModelManager::ensureUpdated()
{
300
    QMutexLocker locker(&m_projectMutex);
301
    if (!m_dirty)
302 303
        return;

304 305 306 307
    m_projectFiles = internalProjectFiles();
    m_includePaths = internalIncludePaths();
    m_frameworkPaths = internalFrameworkPaths();
    m_definedMacros = internalDefinedMacros();
308 309 310
    m_dirty = false;
}

311
QStringList CppModelManager::internalProjectFiles() const
con's avatar
con committed
312 313
{
    QStringList files;
314
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projectToProjectsInfo);
con's avatar
con committed
315 316
    while (it.hasNext()) {
        it.next();
317
        const ProjectInfo pinfo = it.value();
318
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
319 320
            foreach (const ProjectFile &file, part->files)
                files += file.path;
321
        }
con's avatar
con committed
322
    }
323
    files.removeDuplicates();
con's avatar
con committed
324 325 326
    return files;
}

327
QStringList CppModelManager::internalIncludePaths() const
con's avatar
con committed
328 329
{
    QStringList includePaths;
330
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projectToProjectsInfo);
con's avatar
con committed
331 332
    while (it.hasNext()) {
        it.next();
333
        const ProjectInfo pinfo = it.value();
334
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts())
335 336
            foreach (const QString &path, part->includePaths)
                includePaths.append(CppPreprocessor::cleanPath(path));
con's avatar
con committed
337
    }
338
    includePaths.removeDuplicates();
con's avatar
con committed
339 340 341
    return includePaths;
}

342
QStringList CppModelManager::internalFrameworkPaths() const
con's avatar
con committed
343 344
{
    QStringList frameworkPaths;
345
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projectToProjectsInfo);
con's avatar
con committed
346 347
    while (it.hasNext()) {
        it.next();
348
        const ProjectInfo pinfo = it.value();
349
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts())
350 351
            foreach (const QString &path, part->frameworkPaths)
                frameworkPaths.append(CppPreprocessor::cleanPath(path));
con's avatar
con committed
352
    }
353
    frameworkPaths.removeDuplicates();
con's avatar
con committed
354 355 356
    return frameworkPaths;
}

357
QByteArray CppModelManager::internalDefinedMacros() const
con's avatar
con committed
358 359
{
    QByteArray macros;
360
    QSet<QByteArray> alreadyIn;
361
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projectToProjectsInfo);
con's avatar
con committed
362 363
    while (it.hasNext()) {
        it.next();
364
        const ProjectInfo pinfo = it.value();
365 366 367 368 369 370 371 372 373 374
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
            const QList<QByteArray> defs = part->defines.split('\n');
            foreach (const QByteArray &def, defs) {
                if (!alreadyIn.contains(def)) {
                    macros += def;
                    macros.append('\n');
                    alreadyIn.insert(def);
                }
            }
        }
con's avatar
con committed
375 376 377 378
    }
    return macros;
}

379 380 381 382
/// This method will aquire the mutex!
void CppModelManager::dumpModelManagerConfiguration()
{
    // Tons of debug output...
383 384
    qDebug() << "========= CppModelManager::dumpModelManagerConfiguration ======";
    foreach (const ProjectInfo &pinfo, m_projectToProjectsInfo) {
385
        qDebug() << " for project:"<< pinfo.project().data()->projectFilePath();
386 387
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
            qDebug() << "=== part ===";
388 389 390 391 392 393 394
            const char* cVersion;
            switch (part->cVersion) {
            case ProjectPart::C89: cVersion = "C89"; break;
            case ProjectPart::C99: cVersion = "C99"; break;
            case ProjectPart::C11: cVersion = "C11"; break;
            default: cVersion = "INVALID";
            }
395
            const char* cxxVersion;
396
            switch (part->cxxVersion) {
397 398
            case ProjectPart::CXX98: cxxVersion = "CXX98"; break;
            case ProjectPart::CXX11: cxxVersion = "CXX11"; break;
399 400
            default: cxxVersion = "INVALID";
            }
401 402 403 404 405 406 407
            QStringList cxxExtensions;
            if (part->cxxExtensions & ProjectPart::GnuExtensions)
                cxxExtensions << QLatin1String("GnuExtensions");
            if (part->cxxExtensions & ProjectPart::MicrosoftExtensions)
                cxxExtensions << QLatin1String("MicrosoftExtensions");
            if (part->cxxExtensions & ProjectPart::BorlandExtensions)
                cxxExtensions << QLatin1String("BorlandExtensions");
408
            if (part->cxxExtensions & ProjectPart::OpenMPExtensions)
409
                cxxExtensions << QLatin1String("OpenMP");
410

411 412 413
            qDebug() << "cVersion:" << cVersion;
            qDebug() << "cxxVersion:" << cxxVersion;
            qDebug() << "cxxExtensions:" << cxxExtensions;
414 415 416 417 418
            qDebug() << "Qt version:" << part->qtVersion;
            qDebug() << "precompiled header:" << part->precompiledHeaders;
            qDebug() << "defines:" << part->defines;
            qDebug() << "includes:" << part->includePaths;
            qDebug() << "frameworkPaths:" << part->frameworkPaths;
419
            qDebug() << "files:" << part->files;
420 421 422 423 424 425 426 427 428 429 430 431 432
            qDebug() << "";
        }
    }

    ensureUpdated();
    qDebug() << "=== Merged include paths ===";
    foreach (const QString &inc, m_includePaths)
        qDebug() << inc;
    qDebug() << "=== Merged framework paths ===";
    foreach (const QString &inc, m_frameworkPaths)
        qDebug() << inc;
    qDebug() << "=== Merged defined macros ===";
    qDebug() << m_definedMacros;
433
    qDebug() << "========= End of dump ======";
434 435
}

436
void CppModelManager::addExtraEditorSupport(AbstractEditorSupport *editorSupport)
437
{
438
    m_extraEditorSupports.insert(editorSupport);
439 440
}

441
void CppModelManager::removeExtraEditorSupport(AbstractEditorSupport *editorSupport)
442
{
443
    m_extraEditorSupports.remove(editorSupport);
444 445
}

446 447
/// \brief Returns the \c CppEditorSupport for the given text editor. It will
///        create one when none exists yet.
448
CppEditorSupport *CppModelManager::cppEditorSupport(TextEditor::BaseTextEditor *textEditor)
449
{
450
    Q_ASSERT(textEditor);
451

452
    QMutexLocker locker(&m_cppEditorSupportsMutex);
453

454
    CppEditorSupport *editorSupport = m_cppEditorSupports.value(textEditor, 0);
455
    if (!editorSupport) {
456 457
        editorSupport = new CppEditorSupport(this, textEditor);
        m_cppEditorSupports.insert(textEditor, editorSupport);
458 459 460 461
    }
    return editorSupport;
}

462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486
/// \brief Removes the CppEditorSupport for the closed editor.
void CppModelManager::deleteCppEditorSupport(TextEditor::BaseTextEditor *textEditor)
{
    static short numberOfClosedEditors = 0;

    QTC_ASSERT(textEditor, return);

    if (!isCppEditor(textEditor))
        return;

    CppEditorSupport *editorSupport;
    int numberOfOpenEditors = 0;

    { // Only lock the operations on m_cppEditorSupport
        QMutexLocker locker(&m_cppEditorSupportsMutex);
        editorSupport = m_cppEditorSupports.value(textEditor, 0);
        m_cppEditorSupports.remove(textEditor);
        numberOfOpenEditors = m_cppEditorSupports.size();
    }

    delete editorSupport;

    ++numberOfClosedEditors;
    if (numberOfOpenEditors == 0 || numberOfClosedEditors == 5) {
        numberOfClosedEditors = 0;
487
        delayedGC();
488 489 490
    }
}

491
QList<int> CppModelManager::references(CPlusPlus::Symbol *symbol, const LookupContext &context)
492
{
493
    return m_findReferences->references(symbol, context);
494 495
}

496
void CppModelManager::findUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context)
Roberto Raggi's avatar
Roberto Raggi committed
497
{
498
    if (symbol->identifier())
499
        m_findReferences->findUsages(symbol, context);
500 501
}

502 503
void CppModelManager::renameUsages(CPlusPlus::Symbol *symbol,
                                   const CPlusPlus::LookupContext &context,
504
                                   const QString &replacement)
505 506
{
    if (symbol->identifier())
507
        m_findReferences->renameUsages(symbol, context, replacement);
Roberto Raggi's avatar
Roberto Raggi committed
508 509
}

Christian Kamm's avatar
Christian Kamm committed
510 511 512 513 514
void CppModelManager::findMacroUsages(const CPlusPlus::Macro &macro)
{
    m_findReferences->findMacroUses(macro);
}

515 516 517 518 519
void CppModelManager::renameMacroUsages(const CPlusPlus::Macro &macro, const QString &replacement)
{
    m_findReferences->renameMacroUses(macro, replacement);
}

520 521
void CppModelManager::replaceSnapshot(const CPlusPlus::Snapshot &newSnapshot)
{
522
    QMutexLocker snapshotLocker(&m_snapshotMutex);
523 524 525
    m_snapshot = newSnapshot;
}

526
CppModelManager::WorkingCopy CppModelManager::buildWorkingCopyList()
con's avatar
con committed
527
{
528
    QList<CppEditorSupport *> cppEditorSupports;
529 530

    {
531 532
        QMutexLocker locker(&m_cppEditorSupportsMutex);
        cppEditorSupports = m_cppEditorSupports.values();
533 534
    }

535
    WorkingCopy workingCopy;
536
    foreach (const CppEditorSupport *editorSupport, cppEditorSupports) {
537 538
        workingCopy.insert(editorSupport->fileName(), editorSupport->contents(),
                           editorSupport->editorRevision());
con's avatar
con committed
539 540
    }

541 542 543
    QSetIterator<AbstractEditorSupport *> it(m_extraEditorSupports);
    while (it.hasNext()) {
        AbstractEditorSupport *es = it.next();
544
        workingCopy.insert(es->fileName(), es->contents());
545 546
    }

547
    // Add the project configuration file
548
    QByteArray conf = QByteArray::fromRawData(pp_configuration, qstrlen(pp_configuration));
con's avatar
con committed
549
    conf += definedMacros();
550
    workingCopy.insert(configurationFileName(), conf);
con's avatar
con committed
551 552 553 554

    return workingCopy;
}

555
CppModelManager::WorkingCopy CppModelManager::workingCopy() const
556 557 558 559
{
    return const_cast<CppModelManager *>(this)->buildWorkingCopyList();
}

560 561
QFuture<void> CppModelManager::updateSourceFiles(const QStringList &sourceFiles,
                                                 ProgressNotificationMode mode)
562 563 564 565
{
    if (sourceFiles.isEmpty() || !m_indexerEnabled)
        return QFuture<void>();

566
    if (m_indexingSupporter)
567 568
        m_indexingSupporter->refreshSourceFiles(sourceFiles, mode);
    return m_internalIndexingSupport->refreshSourceFiles(sourceFiles, mode);
569
}
con's avatar
con committed
570

571 572
QList<CppModelManager::ProjectInfo> CppModelManager::projectInfos() const
{
573
    QMutexLocker locker(&m_projectMutex);
574
    return m_projectToProjectsInfo.values();
575 576 577 578
}

CppModelManager::ProjectInfo CppModelManager::projectInfo(ProjectExplorer::Project *project) const
{
579
    QMutexLocker locker(&m_projectMutex);
580
    return m_projectToProjectsInfo.value(project, ProjectInfo(project));
581 582
}

583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608
/// \brief Remove all files and their includes (recursively) of given ProjectInfo from the snapshot.
void CppModelManager::removeProjectInfoFilesAndIncludesFromSnapshot(const ProjectInfo &projectInfo)
{
    if (!projectInfo.isValid())
        return;

    QMutexLocker snapshotLocker(&m_snapshotMutex);
    foreach (const ProjectPart::Ptr &projectPart, projectInfo.projectParts()) {
        foreach (const ProjectFile &cxxFile, projectPart->files) {
            foreach (const QString &fileName, m_snapshot.allIncludesForDocument(cxxFile.path))
                m_snapshot.remove(fileName);
            m_snapshot.remove(cxxFile.path);
        }
    }
}

/// \brief Remove all given files from the snapshot.
void CppModelManager::removeFilesFromSnapshot(const QSet<QString> &filesToRemove)
{
    QMutexLocker snapshotLocker(&m_snapshotMutex);
    QSetIterator<QString> i(filesToRemove);
    while (i.hasNext())
        m_snapshot.remove(i.next());
}

class ProjectInfoComparer
609
{
610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677
public:
    ProjectInfoComparer(const CppModelManager::ProjectInfo &oldProjectInfo,
                        const CppModelManager::ProjectInfo &newProjectInfo)
        : m_old(oldProjectInfo)
        , m_oldSourceFiles(oldProjectInfo.sourceFiles().toSet())
        , m_new(newProjectInfo)
        , m_newSourceFiles(newProjectInfo.sourceFiles().toSet())
    {}

    bool definesChanged() const
    {
        return m_new.defines() != m_old.defines();
    }

    bool configurationChanged() const
    {
        return definesChanged()
            || m_new.includePaths() != m_old.includePaths()
            || m_new.frameworkPaths() != m_old.frameworkPaths();
    }

    bool nothingChanged() const
    {
        return !configurationChanged() && m_new.sourceFiles() == m_old.sourceFiles();
    }

    QSet<QString> addedFiles() const
    {
        QSet<QString> addedFilesSet = m_newSourceFiles;
        addedFilesSet.subtract(m_oldSourceFiles);
        return addedFilesSet;
    }

    QSet<QString> removedFiles() const
    {
        QSet<QString> removedFilesSet = m_oldSourceFiles;
        removedFilesSet.subtract(m_newSourceFiles);
        return removedFilesSet;
    }

    /// Returns a list of common files that have a changed timestamp.
    QSet<QString> timeStampModifiedFiles(const Snapshot &snapshot) const
    {
        QSet<QString> commonSourceFiles = m_newSourceFiles;
        commonSourceFiles.intersect(m_oldSourceFiles);

        QList<Document::Ptr> documentsToCheck;
        QSetIterator<QString> i(commonSourceFiles);
        while (i.hasNext()) {
            const QString file = i.next();
            if (Document::Ptr document = snapshot.document(file))
                documentsToCheck << document;
        }

        return CppModelManager::timeStampModifiedFiles(documentsToCheck).toSet();
    }

private:
    const CppModelManager::ProjectInfo &m_old;
    const QSet<QString> m_oldSourceFiles;

    const CppModelManager::ProjectInfo &m_new;
    const QSet<QString> m_newSourceFiles;
};

QFuture<void> CppModelManager::updateProjectInfo(const ProjectInfo &newProjectInfo)
{
    if (!newProjectInfo.isValid())
678
        return QFuture<void>();
679

680 681 682
    QStringList filesToReindex;
    bool filesRemoved = false;

683
    { // Only hold the mutex for a limited scope, so the dumping afterwards does not deadlock.
684 685 686 687
        QMutexLocker projectLocker(&m_projectMutex);

        ProjectExplorer::Project *project = newProjectInfo.project().data();
        const QStringList newSourceFiles = newProjectInfo.sourceFiles();
688

689
        // Check if we can avoid a full reindexing
690
        ProjectInfo oldProjectInfo = m_projectToProjectsInfo.value(project);
691
        if (oldProjectInfo.isValid()) {
692 693
            ProjectInfoComparer comparer(oldProjectInfo, newProjectInfo);
            if (comparer.nothingChanged())
694 695
                return QFuture<void>();

696 697 698 699 700 701 702 703 704
            // If the project configuration changed, do a full reindexing
            if (comparer.configurationChanged()) {
                removeProjectInfoFilesAndIncludesFromSnapshot(oldProjectInfo);
                filesToReindex << newSourceFiles;

                // The "configuration file" includes all defines and therefore should be updated
                if (comparer.definesChanged()) {
                    QMutexLocker snapshotLocker(&m_snapshotMutex);
                    m_snapshot.remove(configurationFileName());
705
                }
706 707 708 709 710 711 712 713

            // Otherwise check for added and modified files
            } else {
                const QSet<QString> addedFiles = comparer.addedFiles();
                filesToReindex << addedFiles.toList();

                const QSet<QString> modifiedFiles = comparer.timeStampModifiedFiles(snapshot());
                filesToReindex << modifiedFiles.toList();
714
            }
715 716 717 718 719 720 721 722 723 724 725 726

            // Announce and purge the removed files from the snapshot
            const QSet<QString> removedFiles = comparer.removedFiles();
            if (!removedFiles.isEmpty()) {
                filesRemoved = true;
                emit aboutToRemoveFiles(removedFiles.toList());
                removeFilesFromSnapshot(removedFiles);
            }

        // A new project was opened/created, do a full indexing
        } else {
            filesToReindex << newSourceFiles;
727 728
        }

729
        // Update Project/ProjectInfo and File/ProjectPart table
730
        m_dirty = true;
731
        m_projectToProjectsInfo.insert(project, newProjectInfo);
732 733
        m_fileToProjectParts.clear();
        foreach (const ProjectInfo &projectInfo, m_projectToProjectsInfo) {
734
            foreach (const ProjectPart::Ptr &projectPart, projectInfo.projectParts()) {
735
                foreach (const ProjectFile &cxxFile, projectPart->files) {
736
                    m_fileToProjectParts[cxxFile.path].append(projectPart);
737
                }
738 739
            }
        }
740

741 742 743
    } // Mutex scope

    // If requested, dump everything we got
744 745
    if (!qgetenv("QTCREATOR_DUMP_PROJECT_INFO").isEmpty())
        dumpModelManagerConfiguration();
746

747 748 749 750 751
    // Remove files from snapshot that are not reachable any more
    if (filesRemoved)
        GC();

    emit projectPartsUpdated(newProjectInfo.project().data());
752

753 754
    // Trigger reindexing
    return updateSourceFiles(filesToReindex, ForcedProgressNotification);
755 756
}

757
QList<ProjectPart::Ptr> CppModelManager::projectPart(const QString &fileName) const
758
{
759
    QList<ProjectPart::Ptr> parts = m_fileToProjectParts.value(fileName);
760 761 762 763 764
    if (!parts.isEmpty())
        return parts;

    DependencyTable table;
    table.build(snapshot());
765
    const QStringList deps = table.filesDependingOn(fileName);
766
    foreach (const QString &dep, deps) {
767
        parts = m_fileToProjectParts.value(dep);
768 769 770 771 772
        if (!parts.isEmpty())
            return parts;
    }

    return parts;
773
}
con's avatar
con committed
774

775 776 777 778 779 780 781 782 783 784 785 786 787 788 789
ProjectPart::Ptr CppModelManager::fallbackProjectPart() const
{
    ProjectPart::Ptr part(new ProjectPart);

    part->defines = m_definedMacros;
    part->includePaths = m_includePaths;
    part->frameworkPaths = m_frameworkPaths;
    part->cVersion = ProjectPart::C11;
    part->cxxVersion = ProjectPart::CXX11;
    part->cxxExtensions = ProjectPart::AllExtensions;
    part->qtVersion = ProjectPart::Qt5;

    return part;
}

con's avatar
con committed
790 791
bool CppModelManager::isCppEditor(Core::IEditor *editor) const
{
792
    return editor->context().contains(ProjectExplorer::Constants::LANG_CXX);
con's avatar
con committed
793 794 795 796
}

void CppModelManager::emitDocumentUpdated(Document::Ptr doc)
{
797
    if (replaceDocument(doc))
798
        emit documentUpdated(doc);
Roberto Raggi's avatar
Roberto Raggi committed
799 800
}

801 802
void CppModelManager::onProjectAdded(ProjectExplorer::Project *)
{
803
    QMutexLocker locker(&m_projectMutex);
804 805 806
    m_dirty = true;
}

807 808 809 810 811
void CppModelManager::delayedGC()
{
    m_delayedGcTimer->start(500);
}

con's avatar
con committed
812 813
void CppModelManager::onAboutToRemoveProject(ProjectExplorer::Project *project)
{
814
    do {
815
        QMutexLocker locker(&m_projectMutex);
816
        m_dirty = true;
817
        m_projectToProjectsInfo.remove(project);
818 819
    } while (0);

820 821 822 823 824 825 826
    delayedGC();
}

void CppModelManager::onAboutToLoadSession()
{
    if (m_delayedGcTimer->isActive())
        m_delayedGcTimer->stop();
con's avatar
con committed
827 828 829
    GC();
}

830
void CppModelManager::onAboutToUnloadSession()
con's avatar
con committed
831
{
832
    Core::ProgressManager::cancelTasks(CppTools::Constants::TASK_INDEX);
833
    do {
834
        QMutexLocker locker(&m_projectMutex);
835
        m_projectToProjectsInfo.clear();
836 837
        m_dirty = true;
    } while (0);
con's avatar
con committed
838 839
}

840 841 842 843 844
void CppModelManager::onCoreAboutToClose()
{
    m_enableGC = false;
}

con's avatar
con committed
845 846
void CppModelManager::GC()
{
847 848 849
    if (!m_enableGC)
        return;

850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866
    // Collect files of CppEditorSupport and AbstractEditorSupport.
    QStringList filesInEditorSupports;
    QList<CppEditorSupport *> cppEditorSupports;
    {
        QMutexLocker locker(&m_cppEditorSupportsMutex);
        cppEditorSupports = m_cppEditorSupports.values();
    }
    foreach (const CppEditorSupport *cppEditorSupport, cppEditorSupports)
        filesInEditorSupports << cppEditorSupport->fileName();

    QSetIterator<AbstractEditorSupport *> jt(m_extraEditorSupports);
    while (jt.hasNext()) {
        AbstractEditorSupport *abstractEditorSupport = jt.next();
        filesInEditorSupports << abstractEditorSupport->fileName();
    }

    Snapshot currentSnapshot = snapshot();
867
    QSet<QString> reachableFiles;
868 869 870 871
    // The configuration file is part of the project files, which is just fine.
    // If single files are open, without any project, then there is no need to
    // keep the configuration file around.
    QStringList todo = filesInEditorSupports + projectFiles();
con's avatar
con committed
872

873
    // Collect all files that are reachable from the project files
874
    while (!todo.isEmpty()) {
875
        const QString file = todo.last();
con's avatar
con committed
876 877
        todo.removeLast();

878
        if (reachableFiles.contains(file))
con's avatar
con committed
879
            continue;
880
        reachableFiles.insert(file);
con's avatar
con committed
881

882
        if (Document::Ptr doc = currentSnapshot.document(file))
con's avatar
con committed
883 884 885
            todo += doc->includedFiles();
    }

886 887
    // Find out the files in the current snapshot that are not reachable from the project files
    QStringList notReachableFiles;
888 889 890 891
    Snapshot newSnapshot;
    for (Snapshot::const_iterator it = currentSnapshot.begin(); it != currentSnapshot.end(); ++it) {
        const QString fileName = it.key();

892
        if (reachableFiles.contains(fileName))
893 894
            newSnapshot.insert(it.value());
        else
895
            notReachableFiles.append(fileName);
con's avatar
con committed
896 897
    }

898 899
    // Announce removing files and replace the snapshot
    emit aboutToRemoveFiles(notReachableFiles);
900
    replaceSnapshot(newSnapshot);
901
    emit gcFinished();
con's avatar
con committed
902
}
903

904 905 906 907 908
void CppModelManager::finishedRefreshingSourceFiles(const QStringList &files)
{
    emit sourceFilesRefreshed(files);
}

909
void CppModelManager::addModelManagerSupport(ModelManagerSupport *modelManagerSupport)
910
{
911 912 913
    if (!m_codeModelSupporters.contains(modelManagerSupport))
        m_codeModelSupporters.append(modelManagerSupport);
}
914

915 916 917
ModelManagerSupport *CppModelManager::modelManagerSupportForMimeType(const QString &mimeType) const
{
    return m_mimeTypeToCodeModelSupport.value(mimeType, m_modelManagerSupportFallback.data());
918 919
}

920
CppCompletionAssistProvider *CppModelManager::completionAssistProvider(Core::IEditor *editor) const
921
{
922 923 924
    ModelManagerSupport *cms = modelManagerSupportForMimeType(editor->document()->mimeType());

    return cms->completionAssistProvider();
925 926
}

927 928
CppHighlightingSupport *CppModelManager::highlightingSupport(Core::IEditor *editor) const
{
929 930
    TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor *>(editor);
    if (!textEditor)
931
        return 0;
932

933 934 935
    ModelManagerSupport *cms = modelManagerSupportForMimeType(editor->document()->mimeType());

    return cms->highlightingSupport(textEditor);
936 937
}

938
void CppModelManager::setIndexingSupport(CppIndexingSupport *indexingSupport)
939 940
{
    if (indexingSupport)
941 942 943 944 945 946
        m_indexingSupporter = indexingSupport;
}

CppIndexingSupport *CppModelManager::indexingSupport()
{
    return m_indexingSupporter ? m_indexingSupporter : m_internalIndexingSupport;
947 948
}

949 950
void CppModelManager::setExtraDiagnostics(const QString &fileName,
                                          const QString &kind,
951 952
                                          const QList<Document::DiagnosticMessage> &diagnostics)
{
953
    QList<CppEditorSupport *> cppEditorSupports;
954

955
    {
956 957
        QMutexLocker locker(&m_cppEditorSupportsMutex);
        cppEditorSupports = m_cppEditorSupports.values();
958 959
    }

960 961 962
    foreach (CppEditorSupport *editorSupport, cppEditorSupports) {
        if (editorSupport->fileName() == fileName) {
            editorSupport->setExtraDiagnostics(kind, diagnostics);
963 964
            break;
        }
965 966
    }
}
967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984

void CppModelManager::setIfdefedOutBlocks(const QString &fileName,
                                          const QList<TextEditor::BlockRange> &ifdeffedOutBlocks)
{
    QList<CppEditorSupport *> cppEditorSupports;

    {
        QMutexLocker locker(&m_cppEditorSupportsMutex);
        cppEditorSupports = m_cppEditorSupports.values();
    }

    foreach (CppEditorSupport *editorSupport, cppEditorSupports) {
        if (editorSupport->fileName() == fileName) {
            editorSupport->setIfdefedOutBlocks(ifdeffedOutBlocks);
            break;
        }
    }
}