cppmodelmanager.cpp 30 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 "cpphighlightingsupport.h"
38
#include "cppindexingsupport.h"
39
#include "cppmodelmanagersupportinternal.h"
40
#include "cppsourceprocessor.h"
41
#include "cpptoolsconstants.h"
42
#include "cpptoolseditorsupport.h"
43
#include "cpptoolsplugin.h"
con's avatar
con committed
44

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

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

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

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

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

70
#ifdef QTCREATOR_WITH_DUMP_AST
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 109

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

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

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

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

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

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

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

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

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

160 161 162 163 164 165 166 167 168 169 170 171
    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);
172 173
}

con's avatar
con committed
174 175
/*!
    \class CppTools::CppModelManager
176 177 178
    \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
179

180 181 182 183
    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
184 185
*/

186 187
QMutex CppModelManager::m_instanceMutex;
CppModelManager *CppModelManager::m_instance = 0;
188 189 190

CppModelManager *CppModelManager::instance()
{
191 192 193 194 195 196 197 198
    if (m_instance)
        return m_instance;

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

    return m_instance;
199 200
}

201
CppModelManager::CppModelManager(QObject *parent)
202
    : CppModelManagerInterface(parent)
203
    , m_indexingSupporter(0)
204
    , m_enableGC(true)
con's avatar
con committed
205
{
Erik Verbruggen's avatar
Erik Verbruggen committed
206 207 208 209 210
    connect(this, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)),
            this, SIGNAL(globalSnapshotChanged()));
    connect(this, SIGNAL(aboutToRemoveFiles(QStringList)),
            this, SIGNAL(globalSnapshotChanged()));

Roberto Raggi's avatar
Roberto Raggi committed
211
    m_findReferences = new CppFindReferences(this);
212
    m_indexerEnabled = qgetenv("QTC_NO_CODE_INDEXER") != "1";
Roberto Raggi's avatar
Roberto Raggi committed
213

214 215
    m_dirty = true;

216 217 218 219
    m_delayedGcTimer = new QTimer(this);
    m_delayedGcTimer->setSingleShot(true);
    connect(m_delayedGcTimer, SIGNAL(timeout()), this, SLOT(GC()));

hjk's avatar
hjk committed
220 221
    QObject *sessionManager = ProjectExplorer::SessionManager::instance();
    connect(sessionManager, SIGNAL(projectAdded(ProjectExplorer::Project*)),
222
            this, SLOT(onProjectAdded(ProjectExplorer::Project*)));
hjk's avatar
hjk committed
223
    connect(sessionManager, SIGNAL(aboutToRemoveProject(ProjectExplorer::Project*)),
Robert Loehning's avatar
Robert Loehning committed
224
            this, SLOT(onAboutToRemoveProject(ProjectExplorer::Project*)));
hjk's avatar
hjk committed
225
    connect(sessionManager, SIGNAL(aboutToLoadSession(QString)),
226
            this, SLOT(onAboutToLoadSession()));
hjk's avatar
hjk committed
227
    connect(sessionManager, SIGNAL(aboutToUnloadSession(QString)),
228
            this, SLOT(onAboutToUnloadSession()));
con's avatar
con committed
229

230 231 232
    connect(Core::ICore::instance(), SIGNAL(coreAboutToClose()),
            this, SLOT(onCoreAboutToClose()));

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

235
    m_modelManagerSupportFallback.reset(new ModelManagerSupportInternal);
236 237
    CppToolsPlugin::instance()->codeModelSettings()->setDefaultId(
                m_modelManagerSupportFallback->id());
238 239
    addModelManagerSupport(m_modelManagerSupportFallback.data());

240
    m_internalIndexingSupport = new BuiltinIndexingSupport;
con's avatar
con committed
241 242 243
}

CppModelManager::~CppModelManager()
244
{
245
    delete m_internalIndexingSupport;
246
}
con's avatar
con committed
247

248
Snapshot CppModelManager::snapshot() const
249
{
250
    QMutexLocker locker(&m_snapshotMutex);
251 252
    return m_snapshot;
}
con's avatar
con committed
253

254 255
Document::Ptr CppModelManager::document(const QString &fileName) const
{
256
    QMutexLocker locker(&m_snapshotMutex);
257 258 259 260 261 262 263 264
    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)
{
265
    QMutexLocker locker(&m_snapshotMutex);
266 267 268 269 270 271 272 273 274 275

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

276 277
void CppModelManager::ensureUpdated()
{
278
    QMutexLocker locker(&m_projectMutex);
279
    if (!m_dirty)
280 281
        return;

282 283 284 285
    m_projectFiles = internalProjectFiles();
    m_includePaths = internalIncludePaths();
    m_frameworkPaths = internalFrameworkPaths();
    m_definedMacros = internalDefinedMacros();
286 287 288
    m_dirty = false;
}

289
QStringList CppModelManager::internalProjectFiles() const
con's avatar
con committed
290 291
{
    QStringList files;
292
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projectToProjectsInfo);
con's avatar
con committed
293 294
    while (it.hasNext()) {
        it.next();
295
        const ProjectInfo pinfo = it.value();
296
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
297 298
            foreach (const ProjectFile &file, part->files)
                files += file.path;
299
        }
con's avatar
con committed
300
    }
301
    files.removeDuplicates();
con's avatar
con committed
302 303 304
    return files;
}

305
QStringList CppModelManager::internalIncludePaths() const
con's avatar
con committed
306 307
{
    QStringList includePaths;
308
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projectToProjectsInfo);
con's avatar
con committed
309 310
    while (it.hasNext()) {
        it.next();
311
        const ProjectInfo pinfo = it.value();
312
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts())
313
            foreach (const QString &path, part->includePaths)
314
                includePaths.append(CppSourceProcessor::cleanPath(path));
con's avatar
con committed
315
    }
316
    includePaths.removeDuplicates();
con's avatar
con committed
317 318 319
    return includePaths;
}

320
QStringList CppModelManager::internalFrameworkPaths() const
con's avatar
con committed
321 322
{
    QStringList frameworkPaths;
323
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projectToProjectsInfo);
con's avatar
con committed
324 325
    while (it.hasNext()) {
        it.next();
326
        const ProjectInfo pinfo = it.value();
327
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts())
328
            foreach (const QString &path, part->frameworkPaths)
329
                frameworkPaths.append(CppSourceProcessor::cleanPath(path));
con's avatar
con committed
330
    }
331
    frameworkPaths.removeDuplicates();
con's avatar
con committed
332 333 334
    return frameworkPaths;
}

335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
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);
        }
    }
}

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

369
/// This function will acquire mutexes!
370
void CppModelManager::dumpModelManagerConfiguration(const QString &logFileId)
371
{
372 373 374 375
    const Snapshot globalSnapshot = snapshot();
    const QString globalSnapshotTitle
        = QString::fromLatin1("Global/Indexing Snapshot (%1 Documents)").arg(globalSnapshot.size());

376
    CppCodeModelInspector::Dumper dumper(globalSnapshot, logFileId);
377 378 379
    dumper.dumpProjectInfos(projectInfos());
    dumper.dumpSnapshot(globalSnapshot, globalSnapshotTitle, /*isGlobalSnapshot=*/ true);
    dumper.dumpWorkingCopy(workingCopy());
380
    ensureUpdated();
381
    dumper.dumpMergedEntities(m_includePaths, m_frameworkPaths, m_definedMacros);
382 383
}

384
void CppModelManager::addExtraEditorSupport(AbstractEditorSupport *editorSupport)
385
{
386
    m_extraEditorSupports.insert(editorSupport);
387 388
}

389
void CppModelManager::removeExtraEditorSupport(AbstractEditorSupport *editorSupport)
390
{
391
    m_extraEditorSupports.remove(editorSupport);
392 393
}

394 395
/// \brief Returns the \c CppEditorSupport for the given text editor. It will
///        create one when none exists yet.
396
CppEditorSupport *CppModelManager::cppEditorSupport(TextEditor::BaseTextEditor *textEditor)
397
{
398
    Q_ASSERT(textEditor);
399

400
    QMutexLocker locker(&m_cppEditorSupportsMutex);
401

402
    CppEditorSupport *editorSupport = m_cppEditorSupports.value(textEditor, 0);
403
    if (!editorSupport && isCppEditor(textEditor)) {
404 405
        editorSupport = new CppEditorSupport(this, textEditor);
        m_cppEditorSupports.insert(textEditor, editorSupport);
406 407 408 409
    }
    return editorSupport;
}

410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434
/// \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;
435
        delayedGC();
436 437 438
    }
}

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

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

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

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

463 464 465 466 467
void CppModelManager::renameMacroUsages(const CPlusPlus::Macro &macro, const QString &replacement)
{
    m_findReferences->renameMacroUses(macro, replacement);
}

468 469
void CppModelManager::replaceSnapshot(const CPlusPlus::Snapshot &newSnapshot)
{
470
    QMutexLocker snapshotLocker(&m_snapshotMutex);
471 472 473
    m_snapshot = newSnapshot;
}

474
CppModelManager::WorkingCopy CppModelManager::buildWorkingCopyList()
con's avatar
con committed
475
{
476
    WorkingCopy workingCopy;
477 478

    foreach (const CppEditorSupport *editorSupport, cppEditorSupportList()) {
479 480
        workingCopy.insert(editorSupport->fileName(), editorSupport->contents(),
                           editorSupport->editorRevision());
con's avatar
con committed
481 482
    }

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

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

    return workingCopy;
}

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

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

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

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

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

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

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

546 547 548 549 550 551
QList<CppEditorSupport *> CppModelManager::cppEditorSupportList() const
{
    QMutexLocker locker(&m_cppEditorSupportsMutex);
    return m_cppEditorSupports.values();
}

552 553 554 555 556 557 558 559 560 561
/// \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
562
{
563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 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 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627
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;
};

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 747 748 749 750 751 752 753 754 755
    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
756 757
bool CppModelManager::isCppEditor(Core::IEditor *editor) const
{
758
    return editor->context().contains(ProjectExplorer::Constants::LANG_CXX);
con's avatar
con committed
759 760 761 762
}

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

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

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

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

788 789 790 791 792 793 794
    delayedGC();
}

void CppModelManager::onAboutToLoadSession()
{
    if (m_delayedGcTimer->isActive())
        m_delayedGcTimer->stop();
con's avatar
con committed
795 796 797
    GC();
}

798
void CppModelManager::onAboutToUnloadSession()
con's avatar
con committed
799
{
800
    Core::ProgressManager::cancelTasks(CppTools::Constants::TASK_INDEX);
801
    do {
802
        QMutexLocker locker(&m_projectMutex);
803
        m_projectToProjectsInfo.clear();
804
        recalculateFileToProjectParts();
805 806
        m_dirty = true;
    } while (0);
con's avatar
con committed
807 808
}

809 810 811 812 813
void CppModelManager::onCoreAboutToClose()
{
    m_enableGC = false;
}

con's avatar
con committed
814 815
void CppModelManager::GC()
{
816 817 818
    if (!m_enableGC)
        return;

819 820
    // Collect files of CppEditorSupport and AbstractEditorSupport.
    QStringList filesInEditorSupports;
821
    foreach (const CppEditorSupport *cppEditorSupport, cppEditorSupportList())
822 823 824 825 826 827 828 829 830
        filesInEditorSupports << cppEditorSupport->fileName();

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

    Snapshot currentSnapshot = snapshot();
831
    QSet<QString> reachableFiles;
832 833 834 835
    // 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
836

837
    // Collect all files that are reachable from the project files
838
    while (!todo.isEmpty()) {
839
        const QString file = todo.last();
con's avatar
con committed
840 841
        todo.removeLast();

842
        if (reachableFiles.contains(file))
con's avatar
con committed
843
            continue;
844
        reachableFiles.insert(file);
con's avatar
con committed
845

846
        if (Document::Ptr doc = currentSnapshot.document(file))
con's avatar
con committed
847 848 849
            todo += doc->includedFiles();
    }

850 851
    // Find out the files in the current snapshot that are not reachable from the project files
    QStringList notReachableFiles;
852 853 854 855
    Snapshot newSnapshot;
    for (Snapshot::const_iterator it = currentSnapshot.begin(); it != currentSnapshot.end(); ++it) {
        const QString fileName = it.key();

856
        if (reachableFiles.contains(fileName))
857 858
            newSnapshot.insert(it.value());
        else
859
            notReachableFiles.append(fileName);
con's avatar
con committed
860 861
    }

862 863
    // Announce removing files and replace the snapshot
    emit aboutToRemoveFiles(notReachableFiles);
864
    replaceSnapshot(newSnapshot);
865
    emit gcFinished();
con's avatar
con committed
866
}
867

868 869 870 871 872
void CppModelManager::finishedRefreshingSourceFiles(const QStringList &files)
{
    emit sourceFilesRefreshed(files);
}

873
void CppModelManager::addModelManagerSupport(ModelManagerSupport *modelManagerSupport)
874
{
875 876 877 878
    Q_ASSERT(modelManagerSupport);
    m_idTocodeModelSupporter[modelManagerSupport->id()] = modelManagerSupport;
    QSharedPointer<CppCodeModelSettings> cms = CppToolsPlugin::instance()->codeModelSettings();
    cms->setModelManagerSupports(m_idTocodeModelSupporter.values());
879
}
880

881 882
ModelManagerSupport *CppModelManager::modelManagerSupportForMimeType(const QString &mimeType) const
{
883 884 885
    QSharedPointer<CppCodeModelSettings> cms = CppToolsPlugin::instance()->codeModelSettings();
    const QString &id = cms->modelManagerSupportId(mimeType);
    return m_idTocodeModelSupporter.value(id, m_modelManagerSupportFallback.data());
886 887
}

888
CppCompletionAssistProvider *CppModelManager::completionAssistProvider(Core::IEditor *editor) const
889
{
890 891 892
    ModelManagerSupport *cms = modelManagerSupportForMimeType(editor->document()->mimeType());

    return cms->completionAssistProvider();
893 894
}

895 896
CppHighlightingSupport *CppModelManager::highlightingSupport(Core::IEditor *editor) const
{
897 898
    TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor *>(editor);
    if (!textEditor)
899
        return 0;
900

901 902 903
    ModelManagerSupport *cms = modelManagerSupportForMimeType(editor->document()->mimeType());

    return cms->highlightingSupport(textEditor);
904 905
}

906
void CppModelManager::setIndexingSupport(CppIndexingSupport *indexingSupport)
907 908
{
    if (indexingSupport)
909 910 911 912 913 914
        m_indexingSupporter = indexingSupport;
}

CppIndexingSupport *CppModelManager::indexingSupport()
{
    return m_indexingSupporter ? m_indexingSupporter : m_internalIndexingSupport;
915 916
}

917 918 919 920 921 922
void CppModelManager::enableGarbageCollector(bool enable)
{
    m_delayedGcTimer->stop();
    m_enableGC = enable;
}

923
bool CppModelManager::setExtraDiagnostics(const QString &fileName,
924
                                          const QString &kind,
925 926
                                          const QList<Document::DiagnosticMessage> &diagnostics)
{
927
    foreach (CppEditorSupport *editorSupport, cppEditorSupportList()) {
928 929
        if (editorSupport->fileName() == fileName) {