cppmodelmanager.cpp 29.3 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
3
** Copyright (C) 2014 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 "cppcodemodelinspectordumper.h"
35
#include "cppcodemodelsettings.h"
36
#include "cppfindreferences.h"
37
#include "cppindexingsupport.h"
38
#include "cppmodelmanagersupportinternal.h"
39
#include "cppsourceprocessor.h"
40
#include "cpptoolsconstants.h"
41
#include "cpptoolsplugin.h"
42
#include "editordocumenthandle.h"
con's avatar
con committed
43

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

51 52 53 54
#include <QCoreApplication>
#include <QDebug>
#include <QMutexLocker>
#include <QTextBlock>
55
#include <QTimer>
56

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

63 64
static const bool DumpProjectInfo = qgetenv("QTC_DUMP_PROJECT_INFO") == "1";

65 66
using namespace CppTools;
using namespace CppTools::Internal;
con's avatar
con committed
67 68
using namespace CPlusPlus;

69
#ifdef QTCREATOR_WITH_DUMP_AST
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108

#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
109
#endif // QTCREATOR_WITH_DUMP_AST
110

con's avatar
con committed
111 112
static const char pp_configuration[] =
    "# 1 \"<configuration>\"\n"
113
    "#define Q_CREATOR_RUN 1\n"
con's avatar
con committed
114 115 116 117 118 119
    "#define __cplusplus 1\n"
    "#define __extension__\n"
    "#define __context__\n"
    "#define __range__\n"
    "#define   restrict\n"
    "#define __restrict\n"
120
    "#define __restrict__\n"
con's avatar
con committed
121

122 123 124 125
    "#define __complex__\n"
    "#define __imag__\n"
    "#define __real__\n"

126 127
    "#define __builtin_va_arg(a,b) ((b)0)\n"

con's avatar
con committed
128 129
    // ### add macros for win32
    "#define __cdecl\n"
130
    "#define __stdcall\n"
131
    "#define __thiscall\n"
con's avatar
con committed
132 133 134 135 136
    "#define QT_WA(x) x\n"
    "#define CALLBACK\n"
    "#define STDMETHODCALLTYPE\n"
    "#define __RPC_FAR\n"
    "#define __declspec(a)\n"
137 138 139
    "#define STDMETHOD(method) virtual HRESULT STDMETHODCALLTYPE method\n"
    "#define __try try\n"
    "#define __except catch\n"
140 141 142
    "#define __finally\n"
    "#define __inline inline\n"
    "#define __forceinline inline\n";
con's avatar
con committed
143

144
QStringList CppModelManager::timeStampModifiedFiles(const QList<Document::Ptr> &documentsToCheck)
145 146 147
{
    QStringList sourceFiles;

148
    foreach (const Document::Ptr doc, documentsToCheck) {
149 150
        const QDateTime lastModified = doc->lastModified();

151
        if (!lastModified.isNull()) {
152 153 154 155 156 157 158
            QFileInfo fileInfo(doc->fileName());

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

159 160 161
    return sourceFiles;
}

162 163 164 165 166 167 168 169 170 171 172 173
/*!
 * \brief createSourceProcessor Create a new source processor, which will signal the
 * model manager when a document has been processed.
 *
 * Indexed file is truncated version of fully parsed document: copy of source
 * code and full AST will be dropped when indexing is done.
 *
 * \return a new source processor object, which the caller needs to delete when finished.
 */
CppSourceProcessor *CppModelManager::createSourceProcessor()
{
    CppModelManager *that = instance();
Orgad Shaneh's avatar
Orgad Shaneh committed
174
    return new CppSourceProcessor(that->snapshot(), [that](const Document::Ptr &doc) {
175 176 177 178 179
        that->emitDocumentUpdated(doc);
        doc->releaseSourceAndAST();
    });
}

180 181 182 183 184 185 186 187 188
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);
189 190
}

con's avatar
con committed
191 192
/*!
    \class CppTools::CppModelManager
193 194 195
    \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
196

197 198 199 200
    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
201 202
*/

203 204
QMutex CppModelManager::m_instanceMutex;
CppModelManager *CppModelManager::m_instance = 0;
205 206 207

CppModelManager *CppModelManager::instance()
{
208 209 210 211 212 213 214 215
    if (m_instance)
        return m_instance;

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

    return m_instance;
216 217
}

218
CppModelManager::CppModelManager(QObject *parent)
219
    : CppModelManagerInterface(parent)
220
    , m_indexingSupporter(0)
221
    , m_enableGC(true)
con's avatar
con committed
222
{
Erik Verbruggen's avatar
Erik Verbruggen committed
223 224 225 226
    connect(this, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)),
            this, SIGNAL(globalSnapshotChanged()));
    connect(this, SIGNAL(aboutToRemoveFiles(QStringList)),
            this, SIGNAL(globalSnapshotChanged()));
227 228
    connect(this, SIGNAL(sourceFilesRefreshed(QStringList)),
            this, SLOT(onSourceFilesRefreshed()));
Erik Verbruggen's avatar
Erik Verbruggen committed
229

Roberto Raggi's avatar
Roberto Raggi committed
230
    m_findReferences = new CppFindReferences(this);
231
    m_indexerEnabled = qgetenv("QTC_NO_CODE_INDEXER") != "1";
Roberto Raggi's avatar
Roberto Raggi committed
232

233 234
    m_dirty = true;

235
    m_delayedGcTimer = new QTimer(this);
236
    m_delayedGcTimer->setObjectName(QLatin1String("CppModelManager::m_delayedGcTimer"));
237 238 239
    m_delayedGcTimer->setSingleShot(true);
    connect(m_delayedGcTimer, SIGNAL(timeout()), this, SLOT(GC()));

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

250 251 252
    connect(Core::ICore::instance(), SIGNAL(coreAboutToClose()),
            this, SLOT(onCoreAboutToClose()));

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

255
    m_modelManagerSupportFallback.reset(new ModelManagerSupportInternal);
256 257
    CppToolsPlugin::instance()->codeModelSettings()->setDefaultId(
                m_modelManagerSupportFallback->id());
258 259
    addModelManagerSupport(m_modelManagerSupportFallback.data());

260
    m_internalIndexingSupport = new BuiltinIndexingSupport;
con's avatar
con committed
261 262 263
}

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

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

274 275
Document::Ptr CppModelManager::document(const QString &fileName) const
{
276
    QMutexLocker locker(&m_snapshotMutex);
277 278 279 280 281 282 283 284
    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)
{
285
    QMutexLocker locker(&m_snapshotMutex);
286 287 288 289 290 291 292 293 294 295

    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;
}

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

302
    m_projectFiles = internalProjectFiles();
303
    m_headerPaths = internalHeaderPaths();
304
    m_definedMacros = internalDefinedMacros();
305 306 307
    m_dirty = false;
}

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

324
ProjectPart::HeaderPaths CppModelManager::internalHeaderPaths() const
con's avatar
con committed
325
{
326
    ProjectPart::HeaderPaths headerPaths;
327
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projectToProjectsInfo);
con's avatar
con committed
328 329
    while (it.hasNext()) {
        it.next();
330
        const ProjectInfo pinfo = it.value();
331 332 333 334 335 336 337 338
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
            foreach (const ProjectPart::HeaderPath &path, part->headerPaths) {
                const ProjectPart::HeaderPath hp(CppSourceProcessor::cleanPath(path.path),
                                                 path.type);
                if (!headerPaths.contains(hp))
                    headerPaths += hp;
            }
        }
con's avatar
con committed
339
    }
340
    return headerPaths;
con's avatar
con committed
341 342
}

343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
static void addUnique(const QList<QByteArray> &defs, QByteArray *macros, QSet<QByteArray> *alreadyIn)
{
    Q_ASSERT(macros);
    Q_ASSERT(alreadyIn);

    foreach (const QByteArray &def, defs) {
        if (def.trimmed().isEmpty())
            continue;
        if (!alreadyIn->contains(def)) {
            macros->append(def);
            macros->append('\n');
            alreadyIn->insert(def);
        }
    }
}

359
QByteArray CppModelManager::internalDefinedMacros() const
con's avatar
con committed
360 361
{
    QByteArray macros;
362
    QSet<QByteArray> alreadyIn;
363
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projectToProjectsInfo);
con's avatar
con committed
364 365
    while (it.hasNext()) {
        it.next();
366
        const ProjectInfo pinfo = it.value();
367
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
368 369
            addUnique(part->toolchainDefines.split('\n'), &macros, &alreadyIn);
            addUnique(part->projectDefines.split('\n'), &macros, &alreadyIn);
370
            if (!part->projectConfigFile.isEmpty())
371
                macros += ProjectPart::readProjectConfigFile(part);
372
        }
con's avatar
con committed
373 374 375 376
    }
    return macros;
}

377
/// This function will acquire mutexes!
378
void CppModelManager::dumpModelManagerConfiguration(const QString &logFileId)
379
{
380 381 382 383
    const Snapshot globalSnapshot = snapshot();
    const QString globalSnapshotTitle
        = QString::fromLatin1("Global/Indexing Snapshot (%1 Documents)").arg(globalSnapshot.size());

384
    CppCodeModelInspector::Dumper dumper(globalSnapshot, logFileId);
385 386 387
    dumper.dumpProjectInfos(projectInfos());
    dumper.dumpSnapshot(globalSnapshot, globalSnapshotTitle, /*isGlobalSnapshot=*/ true);
    dumper.dumpWorkingCopy(workingCopy());
388
    ensureUpdated();
389
    dumper.dumpMergedEntities(m_headerPaths, m_definedMacros);
390 391
}

392
void CppModelManager::addExtraEditorSupport(AbstractEditorSupport *editorSupport)
393
{
394
    m_extraEditorSupports.insert(editorSupport);
395 396
}

397
void CppModelManager::removeExtraEditorSupport(AbstractEditorSupport *editorSupport)
398
{
399
    m_extraEditorSupports.remove(editorSupport);
400 401
}

402
EditorDocumentHandle *CppModelManager::editorDocument(const QString &filePath)
403
{
404
    QTC_ASSERT(!filePath.isEmpty(), return 0);
405

406 407
    QMutexLocker locker(&m_cppEditorsMutex);
    return m_cppEditors.value(filePath, 0);
408 409
}

410
void CppModelManager::registerEditorDocument(EditorDocumentHandle *editorDocument)
411
{
412 413 414
    QTC_ASSERT(editorDocument, return);
    const QString filePath = editorDocument->filePath();
    QTC_ASSERT(!filePath.isEmpty(), return);
415

416 417 418 419
    QMutexLocker locker(&m_cppEditorsMutex);
    QTC_ASSERT(m_cppEditors.value(filePath, 0) == 0, return);
    m_cppEditors.insert(filePath, editorDocument);
}
420

421 422 423
void CppModelManager::unregisterEditorDocument(const QString &filePath)
{
    QTC_ASSERT(!filePath.isEmpty(), return);
424

425 426
    static short closedCppDocuments = 0;
    int openCppDocuments = 0;
427

428 429 430 431 432
    {
        QMutexLocker locker(&m_cppEditorsMutex);
        QTC_ASSERT(m_cppEditors.value(filePath, 0), return);
        QTC_CHECK(m_cppEditors.remove(filePath) == 1);
        openCppDocuments = m_cppEditors.size();
433 434
    }

435 436 437
    ++closedCppDocuments;
    if (openCppDocuments == 0 || closedCppDocuments == 5) {
        closedCppDocuments = 0;
438
        delayedGC();
439 440 441
    }
}

442
QList<int> CppModelManager::references(CPlusPlus::Symbol *symbol, const LookupContext &context)
443
{
444
    return m_findReferences->references(symbol, context);
445 446
}

447
void CppModelManager::findUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context)
Roberto Raggi's avatar
Roberto Raggi committed
448
{
449
    if (symbol->identifier())
450
        m_findReferences->findUsages(symbol, context);
451 452
}

453 454
void CppModelManager::renameUsages(CPlusPlus::Symbol *symbol,
                                   const CPlusPlus::LookupContext &context,
455
                                   const QString &replacement)
456 457
{
    if (symbol->identifier())
458
        m_findReferences->renameUsages(symbol, context, replacement);
Roberto Raggi's avatar
Roberto Raggi committed
459 460
}

Christian Kamm's avatar
Christian Kamm committed
461 462 463 464 465
void CppModelManager::findMacroUsages(const CPlusPlus::Macro &macro)
{
    m_findReferences->findMacroUses(macro);
}

466 467 468 469 470
void CppModelManager::renameMacroUsages(const CPlusPlus::Macro &macro, const QString &replacement)
{
    m_findReferences->renameMacroUses(macro, replacement);
}

471 472
void CppModelManager::replaceSnapshot(const CPlusPlus::Snapshot &newSnapshot)
{
473
    QMutexLocker snapshotLocker(&m_snapshotMutex);
474 475 476
    m_snapshot = newSnapshot;
}

477
WorkingCopy CppModelManager::buildWorkingCopyList()
con's avatar
con committed
478
{
479
    WorkingCopy workingCopy;
480

481 482
    foreach (const EditorDocumentHandle *cppEditor, cppEditors())
        workingCopy.insert(cppEditor->filePath(), cppEditor->contents(), cppEditor->revision());
con's avatar
con committed
483

484 485 486
    QSetIterator<AbstractEditorSupport *> it(m_extraEditorSupports);
    while (it.hasNext()) {
        AbstractEditorSupport *es = it.next();
487
        workingCopy.insert(es->fileName(), es->contents(), es->revision());
488 489
    }

490
    // Add the project configuration file
491
    QByteArray conf = codeModelConfiguration();
con's avatar
con committed
492
    conf += definedMacros();
493
    workingCopy.insert(configurationFileName(), conf);
con's avatar
con committed
494 495 496 497

    return workingCopy;
}

498
WorkingCopy CppModelManager::workingCopy() const
499 500 501 502
{
    return const_cast<CppModelManager *>(this)->buildWorkingCopyList();
}

503 504 505 506 507
QByteArray CppModelManager::codeModelConfiguration() const
{
    return QByteArray::fromRawData(pp_configuration, qstrlen(pp_configuration));
}

508 509
QFuture<void> CppModelManager::updateSourceFiles(const QStringList &sourceFiles,
                                                 ProgressNotificationMode mode)
510 511 512 513
{
    if (sourceFiles.isEmpty() || !m_indexerEnabled)
        return QFuture<void>();

514
    if (m_indexingSupporter)
515 516
        m_indexingSupporter->refreshSourceFiles(sourceFiles, mode);
    return m_internalIndexingSupport->refreshSourceFiles(sourceFiles, mode);
517
}
con's avatar
con committed
518

519
QList<ProjectInfo> CppModelManager::projectInfos() const
520
{
521
    QMutexLocker locker(&m_projectMutex);
522
    return m_projectToProjectsInfo.values();
523 524
}

525
ProjectInfo CppModelManager::projectInfo(ProjectExplorer::Project *project) const
526
{
527
    QMutexLocker locker(&m_projectMutex);
528
    return m_projectToProjectsInfo.value(project, ProjectInfo(project));
529 530
}

531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546
/// \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);
        }
    }
}

547
QList<EditorDocumentHandle *> CppModelManager::cppEditors() const
548
{
549 550
    QMutexLocker locker(&m_cppEditorsMutex);
    return m_cppEditors.values();
551 552
}

553 554 555 556 557 558 559 560 561 562
/// \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
563
{
564
public:
565 566
    ProjectInfoComparer(const ProjectInfo &oldProjectInfo,
                        const ProjectInfo &newProjectInfo)
567 568 569 570 571 572 573 574 575 576 577 578 579 580
        : 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()
581
            || m_new.headerPaths() != m_old.headerPaths();
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 609 610 611 612 613 614 615 616 617 618 619 620
    }

    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:
621
    const ProjectInfo &m_old;
622 623
    const QSet<QString> m_oldSourceFiles;

624
    const ProjectInfo &m_new;
625 626 627
    const QSet<QString> m_newSourceFiles;
};

628 629 630
/// Make sure that m_projectMutex is locked when calling this.
void CppModelManager::recalculateFileToProjectParts()
{
631
    m_projectFileToProjectPart.clear();
632 633 634
    m_fileToProjectParts.clear();
    foreach (const ProjectInfo &projectInfo, m_projectToProjectsInfo) {
        foreach (const ProjectPart::Ptr &projectPart, projectInfo.projectParts()) {
635 636
            m_projectFileToProjectPart[projectPart->projectFile] = projectPart;
            foreach (const ProjectFile &cxxFile, projectPart->files)
637
                m_fileToProjectParts[cxxFile.path].append(projectPart);
638

639 640 641 642
        }
    }
}

643 644 645
QFuture<void> CppModelManager::updateProjectInfo(const ProjectInfo &newProjectInfo)
{
    if (!newProjectInfo.isValid())
646
        return QFuture<void>();
647

648 649 650
    QStringList filesToReindex;
    bool filesRemoved = false;

651
    { // Only hold the mutex for a limited scope, so the dumping afterwards does not deadlock.
652 653 654 655
        QMutexLocker projectLocker(&m_projectMutex);

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

657
        // Check if we can avoid a full reindexing
658
        ProjectInfo oldProjectInfo = m_projectToProjectsInfo.value(project);
659
        if (oldProjectInfo.isValid()) {
660 661
            ProjectInfoComparer comparer(oldProjectInfo, newProjectInfo);
            if (comparer.nothingChanged())
662 663
                return QFuture<void>();

664 665 666 667 668 669 670 671 672
            // 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());
673
                }
674 675 676 677 678 679 680 681

            // 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();
682
            }
683 684 685 686 687 688 689 690 691 692 693 694

            // 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;
695 696
        }

697
        // Update Project/ProjectInfo and File/ProjectPart table
698
        m_dirty = true;
699
        m_projectToProjectsInfo.insert(project, newProjectInfo);
700
        recalculateFileToProjectParts();
701

702 703 704
    } // Mutex scope

    // If requested, dump everything we got
705
    if (DumpProjectInfo)
706
        dumpModelManagerConfiguration(QLatin1String("updateProjectInfo"));
707

708 709 710 711 712
    // Remove files from snapshot that are not reachable any more
    if (filesRemoved)
        GC();

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

714 715
    // Trigger reindexing
    return updateSourceFiles(filesToReindex, ForcedProgressNotification);
716 717
}

718 719 720 721 722
ProjectPart::Ptr CppModelManager::projectPartForProjectFile(const QString &projectFile) const
{
    return m_projectFileToProjectPart.value(projectFile);
}

723
QList<ProjectPart::Ptr> CppModelManager::projectPart(const QString &fileName) const
724
{
725
    QMutexLocker locker(&m_projectMutex);
726 727
    return m_fileToProjectParts.value(fileName);
}
728

729 730 731
QList<ProjectPart::Ptr> CppModelManager::projectPartFromDependencies(const QString &fileName) const
{
    QSet<ProjectPart::Ptr> parts;
732 733
    DependencyTable table;
    table.build(snapshot());
734
    const QStringList deps = table.filesDependingOn(fileName);
735 736
    foreach (const QString &dep, deps)
        parts.unite(QSet<ProjectPart::Ptr>::fromList(m_fileToProjectParts.value(dep)));
737

738
    return parts.values();
739
}
con's avatar
con committed
740

741 742 743 744
ProjectPart::Ptr CppModelManager::fallbackProjectPart() const
{
    ProjectPart::Ptr part(new ProjectPart);

745
    part->projectDefines = m_definedMacros;
746
    part->headerPaths = m_headerPaths;
747 748 749 750 751 752 753 754
    part->cVersion = ProjectPart::C11;
    part->cxxVersion = ProjectPart::CXX11;
    part->cxxExtensions = ProjectPart::AllExtensions;
    part->qtVersion = ProjectPart::Qt5;

    return part;
}

con's avatar
con committed
755 756
bool CppModelManager::isCppEditor(Core::IEditor *editor) const
{
757
    return editor->context().contains(ProjectExplorer::Constants::LANG_CXX);
con's avatar
con committed
758 759 760 761
}

void CppModelManager::emitDocumentUpdated(Document::Ptr doc)
{
762
    if (replaceDocument(doc))
763
        emit documentUpdated(doc);
Roberto Raggi's avatar
Roberto Raggi committed
764 765
}

766 767
void CppModelManager::onProjectAdded(ProjectExplorer::Project *)
{
768
    QMutexLocker locker(&m_projectMutex);
769 770 771
    m_dirty = true;
}

772 773
void CppModelManager::delayedGC()
{
774 775
    if (m_enableGC)
        m_delayedGcTimer->start(500);
776 777
}

con's avatar
con committed
778 779
void CppModelManager::onAboutToRemoveProject(ProjectExplorer::Project *project)
{
780
    do {
781
        QMutexLocker locker(&m_projectMutex);
782
        m_dirty = true;
783
        m_projectToProjectsInfo.remove(project);
784
        recalculateFileToProjectParts();
785 786
    } while (0);

787 788 789
    delayedGC();
}

790 791 792 793 794 795 796 797
void CppModelManager::onSourceFilesRefreshed() const
{
    if (BuiltinIndexingSupport::isFindErrorsIndexingActive()) {
        QTimer::singleShot(1, QCoreApplication::instance(), SLOT(quit()));
        qDebug("FindErrorsIndexing: Done, requesting Qt Creator to quit.");
    }
}

798 799 800 801
void CppModelManager::onAboutToLoadSession()
{
    if (m_delayedGcTimer->isActive())
        m_delayedGcTimer->stop();
con's avatar
con committed
802 803 804
    GC();
}

805
void CppModelManager::onAboutToUnloadSession()
con's avatar
con committed
806
{
807
    Core::ProgressManager::cancelTasks(CppTools::Constants::TASK_INDEX);
808
    do {
809
        QMutexLocker locker(&m_projectMutex);
810
        m_projectToProjectsInfo.clear();
811
        recalculateFileToProjectParts();
812 813
        m_dirty = true;
    } while (0);
con's avatar
con committed
814 815
}

816 817 818 819 820
void CppModelManager::onCoreAboutToClose()
{
    m_enableGC = false;
}

con's avatar
con committed
821 822
void CppModelManager::GC()
{
823 824 825
    if (!m_enableGC)
        return;

826 827
    // Collect files of CppEditorSupport and AbstractEditorSupport.
    QStringList filesInEditorSupports;
828 829
    foreach (const EditorDocumentHandle *cppEditor, cppEditors())
        filesInEditorSupports << cppEditor->filePath();
830 831 832 833 834 835 836 837

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

    Snapshot currentSnapshot = snapshot();
838
    QSet<QString> reachableFiles;
839 840 841 842
    // 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
843

844
    // Collect all files that are reachable from the project files
845
    while (!todo.isEmpty()) {
846
        const QString file = todo.last();
con's avatar
con committed
847 848
        todo.removeLast();

849
        if (reachableFiles.contains(file))
con's avatar
con committed
850
            continue;
851
        reachableFiles.insert(file);
con's avatar
con committed
852

853
        if (Document::Ptr doc = currentSnapshot.document(file))
con's avatar
con committed
854 855 856
            todo += doc->includedFiles();
    }

857 858
    // Find out the files in the current snapshot that are not reachable from the project files
    QStringList notReachableFiles;
859 860 861 862
    Snapshot newSnapshot;
    for (Snapshot::const_iterator it = currentSnapshot.begin(); it != currentSnapshot.end(); ++it) {
        const QString fileName = it.key();

863
        if (reachableFiles.contains(fileName))
864 865
            newSnapshot.insert(it.value());
        else
866
            notReachableFiles.append(fileName);
con's avatar
con committed
867 868
    }

869 870
    // Announce removing files and replace the snapshot
    emit aboutToRemoveFiles(notReachableFiles);
871
    replaceSnapshot(newSnapshot);
872
    emit gcFinished();
con's avatar
con committed
873
}
874

875 876 877 878 879
void CppModelManager::finishedRefreshingSourceFiles(const QStringList &files)
{
    emit sourceFilesRefreshed(files);
}

880
void CppModelManager::addModelManagerSupport(ModelManagerSupport *modelManagerSupport)
881
{
882 883 884 885
    Q_ASSERT(modelManagerSupport);
    m_idTocodeModelSupporter[modelManagerSupport->id()] = modelManagerSupport;
    QSharedPointer<CppCodeModelSettings> cms = CppToolsPlugin::instance()->codeModelSettings();
    cms->setModelManagerSupports(m_idTocodeModelSupporter.values());
886
}
887

888 889
ModelManagerSupport *CppModelManager::modelManagerSupportForMimeType(const QString &mimeType) const
{
890 891 892
    QSharedPointer<CppCodeModelSettings> cms = CppToolsPlugin::instance()->codeModelSettings();
    const QString &id = cms->modelManagerSupportId(mimeType);
    return m_idTocodeModelSupporter.value(id, m_modelManagerSupportFallback.data());
893 894
}

895
CppCompletionAssistProvider *CppModelManager::completionAssistProvider(const QString &mimeType) const
896
{
897 898
    if (mimeType.isEmpty())
        return 0;
899

900 901
    ModelManagerSupport *cms = modelManagerSupportForMimeType(mimeType);
    QTC_ASSERT(cms, return 0);
902
    return cms->completionAssistProvider();
903 904
}

905
BaseEditorDocumentProcessor *CppModelManager::editorDocumentProcessor(
906
        TextEditor::BaseTextDocument *baseTextDocument) const
907
{
908 909 910
    QTC_ASSERT(baseTextDocument, return 0);
    ModelManagerSupport *cms = modelManagerSupportForMimeType(baseTextDocument->mimeType());
    QTC_ASSERT(cms, return 0);
911
    return cms->editorDocumentProcessor(baseTextDocument);
912 913
}

914
void CppModelManager::setIndexingSupport(CppIndexingSupport *indexingSupport)
915 916
{
    if (indexingSupport)
917 918 919 920 921 922
        m_indexingSupporter = indexingSupport;
}

CppIndexingSupport *CppModelManager::indexingSupport()
{
    return m_indexingSupporter ? m_indexingSupporter : m_internalIndexingSupport;
923 924
}

925 926 927 928 929
void CppModelManager::enableGarbageCollector(bool enable)
{
    m_delayedGcTimer->stop();
    m_enableGC = enable;
}