cppmodelmanager.cpp 30.5 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"
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 162 163 164 165 166 167 168 169 170
    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);
171 172
}

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

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

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

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

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

    return m_instance;
198 199
}

200
CppModelManager::CppModelManager(QObject *parent)
201
    : CppModelManagerInterface(parent)
202
    , m_indexingSupporter(0)
203
    , m_enableGC(true)
con's avatar
con committed
204
{
Erik Verbruggen's avatar
Erik Verbruggen committed
205 206 207 208 209
    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
210
    m_findReferences = new CppFindReferences(this);
211
    m_indexerEnabled = qgetenv("QTC_NO_CODE_INDEXER") != "1";
Roberto Raggi's avatar
Roberto Raggi committed
212

213 214
    m_dirty = true;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

399
    QMutexLocker locker(&m_cppEditorSupportsMutex);
400

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

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

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

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

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

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

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

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

473
CppModelManager::WorkingCopy CppModelManager::buildWorkingCopyList()
con's avatar
con committed
474
{
475
    QList<CppEditorSupport *> cppEditorSupports;
476 477

    {
478 479
        QMutexLocker locker(&m_cppEditorSupportsMutex);
        cppEditorSupports = m_cppEditorSupports.values();
480 481
    }

482
    WorkingCopy workingCopy;
483
    foreach (const CppEditorSupport *editorSupport, cppEditorSupports) {
484 485
        workingCopy.insert(editorSupport->fileName(), editorSupport->contents(),
                           editorSupport->editorRevision());
con's avatar
con committed
486 487
    }

488 489 490
    QSetIterator<AbstractEditorSupport *> it(m_extraEditorSupports);
    while (it.hasNext()) {
        AbstractEditorSupport *es = it.next();
491
        workingCopy.insert(es->fileName(), es->contents(), es->revision());
492 493
    }

494
    // Add the project configuration file
495
    QByteArray conf = codeModelConfiguration();
con's avatar
con committed
496
    conf += definedMacros();
497
    workingCopy.insert(configurationFileName(), conf);
con's avatar
con committed
498 499 500 501

    return workingCopy;
}

502
CppModelManager::WorkingCopy CppModelManager::workingCopy() const
503 504 505 506
{
    return const_cast<CppModelManager *>(this)->buildWorkingCopyList();
}

507 508 509 510 511
QByteArray CppModelManager::codeModelConfiguration() const
{
    return QByteArray::fromRawData(pp_configuration, qstrlen(pp_configuration));
}

512 513
QFuture<void> CppModelManager::updateSourceFiles(const QStringList &sourceFiles,
                                                 ProgressNotificationMode mode)
514 515 516 517
{
    if (sourceFiles.isEmpty() || !m_indexerEnabled)
        return QFuture<void>();

518
    if (m_indexingSupporter)
519 520
        m_indexingSupporter->refreshSourceFiles(sourceFiles, mode);
    return m_internalIndexingSupport->refreshSourceFiles(sourceFiles, mode);
521
}
con's avatar
con committed
522

523 524
QList<CppModelManager::ProjectInfo> CppModelManager::projectInfos() const
{
525
    QMutexLocker locker(&m_projectMutex);
526
    return m_projectToProjectsInfo.values();
527 528 529 530
}

CppModelManager::ProjectInfo CppModelManager::projectInfo(ProjectExplorer::Project *project) const
{
531
    QMutexLocker locker(&m_projectMutex);
532
    return m_projectToProjectsInfo.value(project, ProjectInfo(project));
533 534
}

535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560
/// \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
561
{
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
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;
};

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

638 639 640 641
        }
    }
}

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

647 648 649
    QStringList filesToReindex;
    bool filesRemoved = false;

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

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

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

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

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

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

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

701 702 703
    } // Mutex scope

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

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

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

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

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

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

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

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

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

744
    part->projectDefines = m_definedMacros;
745 746 747 748 749 750 751 752 753 754
    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
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 790 791 792 793
    delayedGC();
}

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

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

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

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

818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834
    // 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();
835
    QSet<QString> reachableFiles;
836 837 838 839
    // 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
840

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

846
        if (reachableFiles.contains(file))
con's avatar
con committed
847
            continue;
848
        reachableFiles.insert(file);
con's avatar
con committed
849

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

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

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

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

872 873 874 875 876
void CppModelManager::finishedRefreshingSourceFiles(const QStringList &files)
{
    emit sourceFilesRefreshed(files);
}

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

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

892
CppCompletionAssistProvider *CppModelManager::completionAssistProvider(Core::IEditor *editor) const
893
{
894 895 896
    ModelManagerSupport *cms = modelManagerSupportForMimeType(editor->document()->mimeType());

    return cms->completionAssistProvider();
897 898
}

899 900
CppHighlightingSupport *CppModelManager::highlightingSupport(Core::IEditor *editor) const
{
901 902
    TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor *>(editor);
    if (!textEditor)
903
        return 0;
904

905 906 907
    ModelManagerSupport *cms = modelManagerSupportForMimeType(editor->document()->mimeType());

    return cms->highlightingSupport(textEditor);
908 909
}

910
void CppModelManager::setIndexingSupport(CppIndexingSupport *indexingSupport)
911 912
{
    if (indexingSupport)
913 914 915 916 917 918
        m_indexingSupporter = indexingSupport;
}

CppIndexingSupport *CppModelManager::indexingSupport()
{
    return m_indexingSupporter ? m_indexingSupporter : m_internalIndexingSupport;
919 920
}

921 922 923 924 925 926
void CppModelManager::enableGarbageCollector(bool enable)
{
    m_delayedGcTimer->stop();
    m_enableGC = enable;
}

927
bool CppModelManager::setExtraDiagnostics(const QString &fileName,
928
                                          const QString &kind,
929 930
                                          const QList<Document::DiagnosticMessage> &diagnostics)
{
931
    QList<CppEditorSupport *> cppEditorSupports;
932

933
    {
934 935
        QMutexLocker locker(&m_cppEditorSupportsMutex);
        cppEditorSupports = m_cppEditorSupports.values();
936 937
    }

938 939 940
    foreach (CppEditorSupport *editorSupport, cppEditorSupports) {
        if (editorSupport->fileName() == fileName) {
            editorSupport->setExtraDiagnostics(kind, diagnostics);
941
            return true;
942
        }
943
    }
944
    return false;
945
}
946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963

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