cppmodelmanager.cpp 31.2 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
Q_DECLARE_METATYPE(QSet<QString>)

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

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

71
#ifdef QTCREATOR_WITH_DUMP_AST
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 110

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

113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
namespace CppTools {
namespace Internal {

static QMutex m_instanceMutex;
static CppModelManager *m_instance;

class CppModelManagerPrivate
{
public:
    // Snapshot
    mutable QMutex m_snapshotMutex;
    CPlusPlus::Snapshot m_snapshot;

    // Project integration
    mutable QMutex m_projectMutex;
    QMap<ProjectExplorer::Project *, ProjectInfo> m_projectToProjectsInfo;
    QMap<QString, QList<CppTools::ProjectPart::Ptr> > m_fileToProjectParts;
    QMap<QString, CppTools::ProjectPart::Ptr> m_projectFileToProjectPart;
    // The members below are cached/(re)calculated from the projects and/or their project parts
    bool m_dirty;
    QStringList m_projectFiles;
    ProjectPart::HeaderPaths m_headerPaths;
    QByteArray m_definedMacros;

    // Editor integration
    mutable QMutex m_cppEditorsMutex;
    QMap<QString, EditorDocumentHandle *> m_cppEditors;
    QSet<AbstractEditorSupport *> m_extraEditorSupports;

    // Completion & highlighting
    QHash<QString, ModelManagerSupport *> m_idTocodeModelSupporter;
    QScopedPointer<ModelManagerSupport> m_modelManagerSupportFallback;

    // Indexing
    CppIndexingSupport *m_indexingSupporter;
    CppIndexingSupport *m_internalIndexingSupport;
    bool m_indexerEnabled;

    CppFindReferences *m_findReferences;

    bool m_enableGC;
    QTimer m_delayedGcTimer;
};

} // namespace Internal

const char pp_configuration[] =
con's avatar
con committed
160
    "# 1 \"<configuration>\"\n"
161
    "#define Q_CREATOR_RUN 1\n"
con's avatar
con committed
162 163 164 165 166 167
    "#define __cplusplus 1\n"
    "#define __extension__\n"
    "#define __context__\n"
    "#define __range__\n"
    "#define   restrict\n"
    "#define __restrict\n"
168
    "#define __restrict__\n"
con's avatar
con committed
169

170 171 172 173
    "#define __complex__\n"
    "#define __imag__\n"
    "#define __real__\n"

174 175
    "#define __builtin_va_arg(a,b) ((b)0)\n"

con's avatar
con committed
176 177
    // ### add macros for win32
    "#define __cdecl\n"
178
    "#define __stdcall\n"
179
    "#define __thiscall\n"
con's avatar
con committed
180 181 182 183 184
    "#define QT_WA(x) x\n"
    "#define CALLBACK\n"
    "#define STDMETHODCALLTYPE\n"
    "#define __RPC_FAR\n"
    "#define __declspec(a)\n"
185 186 187
    "#define STDMETHOD(method) virtual HRESULT STDMETHODCALLTYPE method\n"
    "#define __try try\n"
    "#define __except catch\n"
188 189 190
    "#define __finally\n"
    "#define __inline inline\n"
    "#define __forceinline inline\n";
con's avatar
con committed
191

192
QSet<QString> CppModelManager::timeStampModifiedFiles(const QList<Document::Ptr> &documentsToCheck)
193
{
194
    QSet<QString> sourceFiles;
195

196
    foreach (const Document::Ptr doc, documentsToCheck) {
197 198
        const QDateTime lastModified = doc->lastModified();

199
        if (!lastModified.isNull()) {
200 201 202
            QFileInfo fileInfo(doc->fileName());

            if (fileInfo.exists() && fileInfo.lastModified() != lastModified)
203
                sourceFiles.insert(doc->fileName());
204 205 206
        }
    }

207 208 209
    return sourceFiles;
}

210 211 212 213 214 215 216 217 218 219 220 221
/*!
 * \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
222
    return new CppSourceProcessor(that->snapshot(), [that](const Document::Ptr &doc) {
223 224 225 226 227
        that->emitDocumentUpdated(doc);
        doc->releaseSourceAndAST();
    });
}

228 229 230 231 232 233 234 235 236 237
QString CppModelManager::editorConfigurationFileName()
{
    return QLatin1String("<per-editor-defines>");
}

QString CppModelManager::configurationFileName()
{
    return CPlusPlus::Preprocessor::configurationFileName;
}

238 239 240 241 242 243 244
void CppModelManager::updateModifiedSourceFiles()
{
    const Snapshot snapshot = this->snapshot();
    QList<Document::Ptr> documentsToCheck;
    foreach (const Document::Ptr document, snapshot)
        documentsToCheck << document;

245
    updateSourceFiles(timeStampModifiedFiles(documentsToCheck));
246 247
}

con's avatar
con committed
248 249
/*!
    \class CppTools::CppModelManager
250 251 252
    \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
253

254 255 256 257
    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
258 259
*/

260 261
CppModelManager *CppModelManager::instance()
{
262 263 264 265 266 267 268 269
    if (m_instance)
        return m_instance;

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

    return m_instance;
270 271
}

272
CppModelManager::CppModelManager(QObject *parent)
273
    : CppModelManagerBase(parent), d(new CppModelManagerPrivate)
con's avatar
con committed
274
{
275 276 277
    d->m_indexingSupporter = 0;
    d->m_enableGC = true;

278
    qRegisterMetaType<QSet<QString> >();
279
    connect(this, SIGNAL(sourceFilesRefreshed(QSet<QString>)),
280
            this, SLOT(onSourceFilesRefreshed()));
Erik Verbruggen's avatar
Erik Verbruggen committed
281

282 283
    d->m_findReferences = new CppFindReferences(this);
    d->m_indexerEnabled = qgetenv("QTC_NO_CODE_INDEXER") != "1";
Roberto Raggi's avatar
Roberto Raggi committed
284

285
    d->m_dirty = true;
286

287 288 289
    d->m_delayedGcTimer.setObjectName(QLatin1String("CppModelManager::m_delayedGcTimer"));
    d->m_delayedGcTimer.setSingleShot(true);
    connect(&d->m_delayedGcTimer, SIGNAL(timeout()), this, SLOT(GC()));
290

hjk's avatar
hjk committed
291 292
    QObject *sessionManager = ProjectExplorer::SessionManager::instance();
    connect(sessionManager, SIGNAL(projectAdded(ProjectExplorer::Project*)),
293
            this, SLOT(onProjectAdded(ProjectExplorer::Project*)));
hjk's avatar
hjk committed
294
    connect(sessionManager, SIGNAL(aboutToRemoveProject(ProjectExplorer::Project*)),
Robert Loehning's avatar
Robert Loehning committed
295
            this, SLOT(onAboutToRemoveProject(ProjectExplorer::Project*)));
hjk's avatar
hjk committed
296
    connect(sessionManager, SIGNAL(aboutToLoadSession(QString)),
297
            this, SLOT(onAboutToLoadSession()));
hjk's avatar
hjk committed
298
    connect(sessionManager, SIGNAL(aboutToUnloadSession(QString)),
299
            this, SLOT(onAboutToUnloadSession()));
con's avatar
con committed
300

301 302 303
    connect(Core::ICore::instance(), SIGNAL(coreAboutToClose()),
            this, SLOT(onCoreAboutToClose()));

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

306
    d->m_modelManagerSupportFallback.reset(new ModelManagerSupportInternal);
307
    CppToolsPlugin::instance()->codeModelSettings()->setDefaultId(
308 309
                d->m_modelManagerSupportFallback->id());
    addModelManagerSupport(d->m_modelManagerSupportFallback.data());
310

311
    d->m_internalIndexingSupport = new BuiltinIndexingSupport;
con's avatar
con committed
312 313 314
}

CppModelManager::~CppModelManager()
315
{
316
    delete d->m_internalIndexingSupport;
317
    delete d;
318
}
con's avatar
con committed
319

320
Snapshot CppModelManager::snapshot() const
321
{
322 323
    QMutexLocker locker(&d->m_snapshotMutex);
    return d->m_snapshot;
324
}
con's avatar
con committed
325

326 327
Document::Ptr CppModelManager::document(const QString &fileName) const
{
328 329
    QMutexLocker locker(&d->m_snapshotMutex);
    return d->m_snapshot.document(fileName);
330 331 332 333 334 335 336
}

/// Replace the document in the snapshot.
///
/// \returns true if successful, false if the new document is out-dated.
bool CppModelManager::replaceDocument(Document::Ptr newDoc)
{
337
    QMutexLocker locker(&d->m_snapshotMutex);
338

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

344
    d->m_snapshot.insert(newDoc);
345 346 347
    return true;
}

348 349
void CppModelManager::ensureUpdated()
{
350 351
    QMutexLocker locker(&d->m_projectMutex);
    if (!d->m_dirty)
352 353
        return;

354 355 356 357
    d->m_projectFiles = internalProjectFiles();
    d->m_headerPaths = internalHeaderPaths();
    d->m_definedMacros = internalDefinedMacros();
    d->m_dirty = false;
358 359
}

360
QStringList CppModelManager::internalProjectFiles() const
con's avatar
con committed
361 362
{
    QStringList files;
363
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(d->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
            foreach (const ProjectFile &file, part->files)
                files += file.path;
370
        }
con's avatar
con committed
371
    }
372
    files.removeDuplicates();
con's avatar
con committed
373 374 375
    return files;
}

376
ProjectPart::HeaderPaths CppModelManager::internalHeaderPaths() const
con's avatar
con committed
377
{
378
    ProjectPart::HeaderPaths headerPaths;
379
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(d->m_projectToProjectsInfo);
con's avatar
con committed
380 381
    while (it.hasNext()) {
        it.next();
382
        const ProjectInfo pinfo = it.value();
383 384 385 386 387 388 389 390
        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
391
    }
392
    return headerPaths;
con's avatar
con committed
393 394
}

395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
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);
        }
    }
}

411
QByteArray CppModelManager::internalDefinedMacros() const
con's avatar
con committed
412 413
{
    QByteArray macros;
414
    QSet<QByteArray> alreadyIn;
415
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(d->m_projectToProjectsInfo);
con's avatar
con committed
416 417
    while (it.hasNext()) {
        it.next();
418
        const ProjectInfo pinfo = it.value();
419
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
420 421
            addUnique(part->toolchainDefines.split('\n'), &macros, &alreadyIn);
            addUnique(part->projectDefines.split('\n'), &macros, &alreadyIn);
422
            if (!part->projectConfigFile.isEmpty())
423
                macros += ProjectPart::readProjectConfigFile(part);
424
        }
con's avatar
con committed
425 426 427 428
    }
    return macros;
}

429
/// This function will acquire mutexes!
430
void CppModelManager::dumpModelManagerConfiguration(const QString &logFileId)
431
{
432 433 434 435
    const Snapshot globalSnapshot = snapshot();
    const QString globalSnapshotTitle
        = QString::fromLatin1("Global/Indexing Snapshot (%1 Documents)").arg(globalSnapshot.size());

436
    CppCodeModelInspector::Dumper dumper(globalSnapshot, logFileId);
437 438 439
    dumper.dumpProjectInfos(projectInfos());
    dumper.dumpSnapshot(globalSnapshot, globalSnapshotTitle, /*isGlobalSnapshot=*/ true);
    dumper.dumpWorkingCopy(workingCopy());
440
    ensureUpdated();
441
    dumper.dumpMergedEntities(d->m_headerPaths, d->m_definedMacros);
442 443
}

444
void CppModelManager::addExtraEditorSupport(AbstractEditorSupport *editorSupport)
445
{
446
    d->m_extraEditorSupports.insert(editorSupport);
447 448
}

449
void CppModelManager::removeExtraEditorSupport(AbstractEditorSupport *editorSupport)
450
{
451
    d->m_extraEditorSupports.remove(editorSupport);
452 453
}

454
EditorDocumentHandle *CppModelManager::editorDocument(const QString &filePath)
455
{
456
    QTC_ASSERT(!filePath.isEmpty(), return 0);
457

458 459
    QMutexLocker locker(&d->m_cppEditorsMutex);
    return d->m_cppEditors.value(filePath, 0);
460 461
}

462
void CppModelManager::registerEditorDocument(EditorDocumentHandle *editorDocument)
463
{
464 465 466
    QTC_ASSERT(editorDocument, return);
    const QString filePath = editorDocument->filePath();
    QTC_ASSERT(!filePath.isEmpty(), return);
467

468 469 470
    QMutexLocker locker(&d->m_cppEditorsMutex);
    QTC_ASSERT(d->m_cppEditors.value(filePath, 0) == 0, return);
    d->m_cppEditors.insert(filePath, editorDocument);
471
}
472

473 474 475
void CppModelManager::unregisterEditorDocument(const QString &filePath)
{
    QTC_ASSERT(!filePath.isEmpty(), return);
476

477 478
    static short closedCppDocuments = 0;
    int openCppDocuments = 0;
479

480
    {
481 482 483 484
        QMutexLocker locker(&d->m_cppEditorsMutex);
        QTC_ASSERT(d->m_cppEditors.value(filePath, 0), return);
        QTC_CHECK(d->m_cppEditors.remove(filePath) == 1);
        openCppDocuments = d->m_cppEditors.size();
485 486
    }

487 488 489
    ++closedCppDocuments;
    if (openCppDocuments == 0 || closedCppDocuments == 5) {
        closedCppDocuments = 0;
490
        delayedGC();
491 492 493
    }
}

494
QList<int> CppModelManager::references(CPlusPlus::Symbol *symbol, const LookupContext &context)
495
{
496
    return d->m_findReferences->references(symbol, context);
497 498
}

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

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

Christian Kamm's avatar
Christian Kamm committed
513 514
void CppModelManager::findMacroUsages(const CPlusPlus::Macro &macro)
{
515
    d->m_findReferences->findMacroUses(macro);
Christian Kamm's avatar
Christian Kamm committed
516 517
}

518 519
void CppModelManager::renameMacroUsages(const CPlusPlus::Macro &macro, const QString &replacement)
{
520
    d->m_findReferences->renameMacroUses(macro, replacement);
521 522
}

523 524
void CppModelManager::replaceSnapshot(const CPlusPlus::Snapshot &newSnapshot)
{
525 526
    QMutexLocker snapshotLocker(&d->m_snapshotMutex);
    d->m_snapshot = newSnapshot;
527 528
}

529
WorkingCopy CppModelManager::buildWorkingCopyList()
con's avatar
con committed
530
{
531
    WorkingCopy workingCopy;
532

533 534
    foreach (const EditorDocumentHandle *cppEditor, cppEditors())
        workingCopy.insert(cppEditor->filePath(), cppEditor->contents(), cppEditor->revision());
con's avatar
con committed
535

536
    QSetIterator<AbstractEditorSupport *> it(d->m_extraEditorSupports);
537 538
    while (it.hasNext()) {
        AbstractEditorSupport *es = it.next();
539
        workingCopy.insert(es->fileName(), es->contents(), es->revision());
540 541
    }

542
    // Add the project configuration file
543
    QByteArray conf = codeModelConfiguration();
con's avatar
con committed
544
    conf += definedMacros();
545
    workingCopy.insert(configurationFileName(), conf);
con's avatar
con committed
546 547 548 549

    return workingCopy;
}

550
WorkingCopy CppModelManager::workingCopy() const
551 552 553 554
{
    return const_cast<CppModelManager *>(this)->buildWorkingCopyList();
}

555 556 557 558 559
QByteArray CppModelManager::codeModelConfiguration() const
{
    return QByteArray::fromRawData(pp_configuration, qstrlen(pp_configuration));
}

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

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

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

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

583 584 585 586 587 588
/// \brief Remove all files and their includes (recursively) of given ProjectInfo from the snapshot.
void CppModelManager::removeProjectInfoFilesAndIncludesFromSnapshot(const ProjectInfo &projectInfo)
{
    if (!projectInfo.isValid())
        return;

589
    QMutexLocker snapshotLocker(&d->m_snapshotMutex);
590 591
    foreach (const ProjectPart::Ptr &projectPart, projectInfo.projectParts()) {
        foreach (const ProjectFile &cxxFile, projectPart->files) {
592 593 594
            foreach (const QString &fileName, d->m_snapshot.allIncludesForDocument(cxxFile.path))
                d->m_snapshot.remove(fileName);
            d->m_snapshot.remove(cxxFile.path);
595 596 597 598
        }
    }
}

599
QList<EditorDocumentHandle *> CppModelManager::cppEditors() const
600
{
601 602
    QMutexLocker locker(&d->m_cppEditorsMutex);
    return d->m_cppEditors.values();
603 604
}

605 606 607
/// \brief Remove all given files from the snapshot.
void CppModelManager::removeFilesFromSnapshot(const QSet<QString> &filesToRemove)
{
608
    QMutexLocker snapshotLocker(&d->m_snapshotMutex);
609 610
    QSetIterator<QString> i(filesToRemove);
    while (i.hasNext())
611
        d->m_snapshot.remove(i.next());
612 613 614
}

class ProjectInfoComparer
615
{
616
public:
617 618
    ProjectInfoComparer(const ProjectInfo &oldProjectInfo,
                        const ProjectInfo &newProjectInfo)
619
        : m_old(oldProjectInfo)
620
        , m_oldSourceFiles(oldProjectInfo.sourceFiles())
621
        , m_new(newProjectInfo)
622
        , m_newSourceFiles(newProjectInfo.sourceFiles())
623 624 625 626 627 628 629 630 631 632
    {}

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

    bool configurationChanged() const
    {
        return definesChanged()
633
            || m_new.headerPaths() != m_old.headerPaths();
634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668
    }

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

669
        return CppModelManager::timeStampModifiedFiles(documentsToCheck);
670 671 672
    }

private:
673
    const ProjectInfo &m_old;
674 675
    const QSet<QString> m_oldSourceFiles;

676
    const ProjectInfo &m_new;
677 678 679
    const QSet<QString> m_newSourceFiles;
};

680 681 682
/// Make sure that m_projectMutex is locked when calling this.
void CppModelManager::recalculateFileToProjectParts()
{
683 684 685
    d->m_projectFileToProjectPart.clear();
    d->m_fileToProjectParts.clear();
    foreach (const ProjectInfo &projectInfo, d->m_projectToProjectsInfo) {
686
        foreach (const ProjectPart::Ptr &projectPart, projectInfo.projectParts()) {
687
            d->m_projectFileToProjectPart[projectPart->projectFile] = projectPart;
688
            foreach (const ProjectFile &cxxFile, projectPart->files)
689
                d->m_fileToProjectParts[cxxFile.path].append(projectPart);
690

691 692 693 694
        }
    }
}

695 696 697
QFuture<void> CppModelManager::updateProjectInfo(const ProjectInfo &newProjectInfo)
{
    if (!newProjectInfo.isValid())
698
        return QFuture<void>();
699

700
    QSet<QString> filesToReindex;
701 702
    bool filesRemoved = false;

703
    { // Only hold the mutex for a limited scope, so the dumping afterwards does not deadlock.
704
        QMutexLocker projectLocker(&d->m_projectMutex);
705 706

        ProjectExplorer::Project *project = newProjectInfo.project().data();
707
        const QSet<QString> newSourceFiles = newProjectInfo.sourceFiles();
708

709
        // Check if we can avoid a full reindexing
710
        ProjectInfo oldProjectInfo = d->m_projectToProjectsInfo.value(project);
711
        if (oldProjectInfo.isValid()) {
712 713
            ProjectInfoComparer comparer(oldProjectInfo, newProjectInfo);
            if (comparer.nothingChanged())
714 715
                return QFuture<void>();

716 717 718
            // If the project configuration changed, do a full reindexing
            if (comparer.configurationChanged()) {
                removeProjectInfoFilesAndIncludesFromSnapshot(oldProjectInfo);
719
                filesToReindex.unite(newSourceFiles);
720 721 722

                // The "configuration file" includes all defines and therefore should be updated
                if (comparer.definesChanged()) {
723 724
                    QMutexLocker snapshotLocker(&d->m_snapshotMutex);
                    d->m_snapshot.remove(configurationFileName());
725
                }
726 727 728 729

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

                const QSet<QString> modifiedFiles = comparer.timeStampModifiedFiles(snapshot());
733
                filesToReindex.unite(modifiedFiles);
734
            }
735 736 737 738 739 740 741 742 743 744 745

            // 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 {
746
            filesToReindex.unite(newSourceFiles);
747 748
        }

749
        // Update Project/ProjectInfo and File/ProjectPart table
750 751
        d->m_dirty = true;
        d->m_projectToProjectsInfo.insert(project, newProjectInfo);
752
        recalculateFileToProjectParts();
753

754 755 756
    } // Mutex scope

    // If requested, dump everything we got
757
    if (DumpProjectInfo)
758
        dumpModelManagerConfiguration(QLatin1String("updateProjectInfo"));
759

760 761 762 763 764
    // Remove files from snapshot that are not reachable any more
    if (filesRemoved)
        GC();

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

766 767
    // Trigger reindexing
    return updateSourceFiles(filesToReindex, ForcedProgressNotification);
768 769
}

770 771
ProjectPart::Ptr CppModelManager::projectPartForProjectFile(const QString &projectFile) const
{
772
    return d->m_projectFileToProjectPart.value(projectFile);
773 774
}

775
QList<ProjectPart::Ptr> CppModelManager::projectPart(const QString &fileName) const
776
{
777 778
    QMutexLocker locker(&d->m_projectMutex);
    return d->m_fileToProjectParts.value(fileName);
779
}
780

781 782 783
QList<ProjectPart::Ptr> CppModelManager::projectPartFromDependencies(const QString &fileName) const
{
    QSet<ProjectPart::Ptr> parts;
784
    const QStringList deps = snapshot().filesDependingOn(fileName);
785
    foreach (const QString &dep, deps)
786
        parts.unite(QSet<ProjectPart::Ptr>::fromList(d->m_fileToProjectParts.value(dep)));
787

788
    return parts.values();
789
}
con's avatar
con committed
790

791 792 793 794
ProjectPart::Ptr CppModelManager::fallbackProjectPart() const
{
    ProjectPart::Ptr part(new ProjectPart);

795 796
    part->projectDefines = d->m_definedMacros;
    part->headerPaths = d->m_headerPaths;
797 798
    part->languageVersion = ProjectPart::CXX14;
    part->languageExtensions = ProjectPart::AllExtensions;
799 800 801 802 803
    part->qtVersion = ProjectPart::Qt5;

    return part;
}

con's avatar
con committed
804 805
bool CppModelManager::isCppEditor(Core::IEditor *editor) const
{
806
    return editor->context().contains(ProjectExplorer::Constants::LANG_CXX);
con's avatar
con committed
807 808 809 810
}

void CppModelManager::emitDocumentUpdated(Document::Ptr doc)
{
811
    if (replaceDocument(doc))
812
        emit documentUpdated(doc);
Roberto Raggi's avatar
Roberto Raggi committed
813 814
}

815 816
void CppModelManager::onProjectAdded(ProjectExplorer::Project *)
{
817 818
    QMutexLocker locker(&d->m_projectMutex);
    d->m_dirty = true;
819 820
}

821 822
void CppModelManager::delayedGC()
{
823 824
    if (d->m_enableGC)
        d->m_delayedGcTimer.start(500);
825 826
}

con's avatar
con committed
827 828
void CppModelManager::onAboutToRemoveProject(ProjectExplorer::Project *project)
{
829
    do {
830 831 832
        QMutexLocker locker(&d->m_projectMutex);
        d->m_dirty = true;
        d->m_projectToProjectsInfo.remove(project);
833
        recalculateFileToProjectParts();
834 835
    } while (0);

836 837 838
    delayedGC();
}

839 840 841 842 843 844 845 846
void CppModelManager::onSourceFilesRefreshed() const
{
    if (BuiltinIndexingSupport::isFindErrorsIndexingActive()) {
        QTimer::singleShot(1, QCoreApplication::instance(), SLOT(quit()));
        qDebug("FindErrorsIndexing: Done, requesting Qt Creator to quit.");
    }
}

847 848
void CppModelManager::onAboutToLoadSession()
{
849 850
    if (d->m_delayedGcTimer.isActive())
        d->m_delayedGcTimer.stop();
con's avatar
con committed
851 852 853
    GC();
}