cppmodelmanager.cpp 34 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
** 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
Eike Ziller's avatar
Eike Ziller committed
13 14
** conditions see http://www.qt.io/licensing.  For further information
** use the contact form at http://www.qt.io/contact-us.
15
**
16
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
** Alternatively, this file may be used under the terms of the GNU Lesser
Eike Ziller's avatar
Eike Ziller committed
18 19 20 21 22 23
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
hjk's avatar
hjk committed
24 25 26
**
** 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
27 28
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
29
****************************************************************************/
hjk's avatar
hjk committed
30

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

33
#include "abstracteditorsupport.h"
34
#include "builtinindexingsupport.h"
35
#include "cppcodemodelinspectordumper.h"
36
#include "cppcodemodelsettings.h"
37
#include "cppfindreferences.h"
38
#include "cppindexingsupport.h"
39
#include "cppmodelmanagersupportinternal.h"
40
#include "cpprefactoringchanges.h"
41
#include "cppsourceprocessor.h"
42
#include "cpptoolsconstants.h"
43
#include "cpptoolsplugin.h"
44
#include "editordocumenthandle.h"
con's avatar
con committed
45

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

54 55 56 57
#include <QCoreApplication>
#include <QDebug>
#include <QMutexLocker>
#include <QTextBlock>
58
#include <QTimer>
59

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

66 67
Q_DECLARE_METATYPE(QSet<QString>)

68 69
static const bool DumpProjectInfo = qgetenv("QTC_DUMP_PROJECT_INFO") == "1";

70 71
using namespace CppTools;
using namespace CppTools::Internal;
con's avatar
con committed
72 73
using namespace CPlusPlus;

74
#ifdef QTCREATOR_WITH_DUMP_AST
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 111 112 113

#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
114
#endif // QTCREATOR_WITH_DUMP_AST
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 160 161 162
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
163
    "# 1 \"<configuration>\"\n"
164
    "#define Q_CREATOR_RUN 1\n"
con's avatar
con committed
165 166 167 168 169 170
    "#define __cplusplus 1\n"
    "#define __extension__\n"
    "#define __context__\n"
    "#define __range__\n"
    "#define   restrict\n"
    "#define __restrict\n"
171
    "#define __restrict__\n"
con's avatar
con committed
172

173 174 175 176
    "#define __complex__\n"
    "#define __imag__\n"
    "#define __real__\n"

177 178
    "#define __builtin_va_arg(a,b) ((b)0)\n"

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

195
QSet<QString> CppModelManager::timeStampModifiedFiles(const QList<Document::Ptr> &documentsToCheck)
196
{
197
    QSet<QString> sourceFiles;
198

199
    foreach (const Document::Ptr doc, documentsToCheck) {
200 201
        const QDateTime lastModified = doc->lastModified();

202
        if (!lastModified.isNull()) {
203 204 205
            QFileInfo fileInfo(doc->fileName());

            if (fileInfo.exists() && fileInfo.lastModified() != lastModified)
206
                sourceFiles.insert(doc->fileName());
207 208 209
        }
    }

210 211 212
    return sourceFiles;
}

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

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

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

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

248
    updateSourceFiles(timeStampModifiedFiles(documentsToCheck));
249 250
}

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

257 258 259 260
    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
261 262
*/

263 264
CppModelManager *CppModelManager::instance()
{
265 266 267 268 269 270 271 272
    if (m_instance)
        return m_instance;

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

    return m_instance;
273 274
}

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

281
    qRegisterMetaType<QSet<QString> >();
282
    connect(this, SIGNAL(sourceFilesRefreshed(QSet<QString>)),
283
            this, SLOT(onSourceFilesRefreshed()));
Erik Verbruggen's avatar
Erik Verbruggen committed
284

285 286
    d->m_findReferences = new CppFindReferences(this);
    d->m_indexerEnabled = qgetenv("QTC_NO_CODE_INDEXER") != "1";
Roberto Raggi's avatar
Roberto Raggi committed
287

288
    d->m_dirty = true;
289

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

hjk's avatar
hjk committed
294 295
    QObject *sessionManager = ProjectExplorer::SessionManager::instance();
    connect(sessionManager, SIGNAL(projectAdded(ProjectExplorer::Project*)),
296
            this, SLOT(onProjectAdded(ProjectExplorer::Project*)));
hjk's avatar
hjk committed
297
    connect(sessionManager, SIGNAL(aboutToRemoveProject(ProjectExplorer::Project*)),
Robert Loehning's avatar
Robert Loehning committed
298
            this, SLOT(onAboutToRemoveProject(ProjectExplorer::Project*)));
hjk's avatar
hjk committed
299
    connect(sessionManager, SIGNAL(aboutToLoadSession(QString)),
300
            this, SLOT(onAboutToLoadSession()));
hjk's avatar
hjk committed
301
    connect(sessionManager, SIGNAL(aboutToUnloadSession(QString)),
302
            this, SLOT(onAboutToUnloadSession()));
303 304
    connect(Core::DocumentManager::instance(), &Core::DocumentManager::allDocumentsRenamed,
            this, &CppModelManager::renameIncludes);
con's avatar
con committed
305

306 307 308
    connect(Core::ICore::instance(), SIGNAL(coreAboutToClose()),
            this, SLOT(onCoreAboutToClose()));

con's avatar
con committed
309
    qRegisterMetaType<CPlusPlus::Document::Ptr>("CPlusPlus::Document::Ptr");
310 311
    qRegisterMetaType<QList<CPlusPlus::Document::DiagnosticMessage>>(
                "QList<CPlusPlus::Document::DiagnosticMessage>");
con's avatar
con committed
312

313
    d->m_modelManagerSupportFallback.reset(new ModelManagerSupportInternal);
314
    CppToolsPlugin::instance()->codeModelSettings()->setDefaultId(
315 316
                d->m_modelManagerSupportFallback->id());
    addModelManagerSupport(d->m_modelManagerSupportFallback.data());
317

318
    d->m_internalIndexingSupport = new BuiltinIndexingSupport;
con's avatar
con committed
319 320 321
}

CppModelManager::~CppModelManager()
322
{
323
    delete d->m_internalIndexingSupport;
324
    delete d;
325
}
con's avatar
con committed
326

327
Snapshot CppModelManager::snapshot() const
328
{
329 330
    QMutexLocker locker(&d->m_snapshotMutex);
    return d->m_snapshot;
331
}
con's avatar
con committed
332

333 334
Document::Ptr CppModelManager::document(const QString &fileName) const
{
335 336
    QMutexLocker locker(&d->m_snapshotMutex);
    return d->m_snapshot.document(fileName);
337 338 339 340 341 342 343
}

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

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

351
    d->m_snapshot.insert(newDoc);
352 353 354
    return true;
}

355 356
void CppModelManager::ensureUpdated()
{
357 358
    QMutexLocker locker(&d->m_projectMutex);
    if (!d->m_dirty)
359 360
        return;

361 362 363 364
    d->m_projectFiles = internalProjectFiles();
    d->m_headerPaths = internalHeaderPaths();
    d->m_definedMacros = internalDefinedMacros();
    d->m_dirty = false;
365 366
}

367
QStringList CppModelManager::internalProjectFiles() const
con's avatar
con committed
368 369
{
    QStringList files;
370
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(d->m_projectToProjectsInfo);
con's avatar
con committed
371 372
    while (it.hasNext()) {
        it.next();
373
        const ProjectInfo pinfo = it.value();
374
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
375 376
            foreach (const ProjectFile &file, part->files)
                files += file.path;
377
        }
con's avatar
con committed
378
    }
379
    files.removeDuplicates();
con's avatar
con committed
380 381 382
    return files;
}

383
ProjectPart::HeaderPaths CppModelManager::internalHeaderPaths() const
con's avatar
con committed
384
{
385
    ProjectPart::HeaderPaths headerPaths;
386
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(d->m_projectToProjectsInfo);
con's avatar
con committed
387 388
    while (it.hasNext()) {
        it.next();
389
        const ProjectInfo pinfo = it.value();
390 391 392 393 394 395 396 397
        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
398
    }
399
    return headerPaths;
con's avatar
con committed
400 401
}

402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417
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);
        }
    }
}

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

436
/// This function will acquire mutexes!
437
void CppModelManager::dumpModelManagerConfiguration(const QString &logFileId)
438
{
439 440 441 442
    const Snapshot globalSnapshot = snapshot();
    const QString globalSnapshotTitle
        = QString::fromLatin1("Global/Indexing Snapshot (%1 Documents)").arg(globalSnapshot.size());

443
    CppCodeModelInspector::Dumper dumper(globalSnapshot, logFileId);
444 445 446
    dumper.dumpProjectInfos(projectInfos());
    dumper.dumpSnapshot(globalSnapshot, globalSnapshotTitle, /*isGlobalSnapshot=*/ true);
    dumper.dumpWorkingCopy(workingCopy());
447
    ensureUpdated();
448
    dumper.dumpMergedEntities(d->m_headerPaths, d->m_definedMacros);
449 450
}

451
void CppModelManager::addExtraEditorSupport(AbstractEditorSupport *editorSupport)
452
{
453
    d->m_extraEditorSupports.insert(editorSupport);
454 455
}

456
void CppModelManager::removeExtraEditorSupport(AbstractEditorSupport *editorSupport)
457
{
458
    d->m_extraEditorSupports.remove(editorSupport);
459 460
}

461
EditorDocumentHandle *CppModelManager::editorDocument(const QString &filePath) const
462
{
463
    QTC_ASSERT(!filePath.isEmpty(), return 0);
464

465 466
    QMutexLocker locker(&d->m_cppEditorsMutex);
    return d->m_cppEditors.value(filePath, 0);
467 468
}

469
void CppModelManager::registerEditorDocument(EditorDocumentHandle *editorDocument)
470
{
471 472 473
    QTC_ASSERT(editorDocument, return);
    const QString filePath = editorDocument->filePath();
    QTC_ASSERT(!filePath.isEmpty(), return);
474

475 476 477
    QMutexLocker locker(&d->m_cppEditorsMutex);
    QTC_ASSERT(d->m_cppEditors.value(filePath, 0) == 0, return);
    d->m_cppEditors.insert(filePath, editorDocument);
478
}
479

480 481 482
void CppModelManager::unregisterEditorDocument(const QString &filePath)
{
    QTC_ASSERT(!filePath.isEmpty(), return);
483

484 485
    static short closedCppDocuments = 0;
    int openCppDocuments = 0;
486

487
    {
488 489 490 491
        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();
492 493
    }

494 495 496
    ++closedCppDocuments;
    if (openCppDocuments == 0 || closedCppDocuments == 5) {
        closedCppDocuments = 0;
497
        delayedGC();
498 499 500
    }
}

501
QList<int> CppModelManager::references(CPlusPlus::Symbol *symbol, const LookupContext &context)
502
{
503
    return d->m_findReferences->references(symbol, context);
504 505
}

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

512 513
void CppModelManager::renameUsages(CPlusPlus::Symbol *symbol,
                                   const CPlusPlus::LookupContext &context,
514
                                   const QString &replacement)
515 516
{
    if (symbol->identifier())
517
        d->m_findReferences->renameUsages(symbol, context, replacement);
Roberto Raggi's avatar
Roberto Raggi committed
518 519
}

Christian Kamm's avatar
Christian Kamm committed
520 521
void CppModelManager::findMacroUsages(const CPlusPlus::Macro &macro)
{
522
    d->m_findReferences->findMacroUses(macro);
Christian Kamm's avatar
Christian Kamm committed
523 524
}

525 526
void CppModelManager::renameMacroUsages(const CPlusPlus::Macro &macro, const QString &replacement)
{
527
    d->m_findReferences->renameMacroUses(macro, replacement);
528 529
}

530 531
void CppModelManager::replaceSnapshot(const CPlusPlus::Snapshot &newSnapshot)
{
532 533
    QMutexLocker snapshotLocker(&d->m_snapshotMutex);
    d->m_snapshot = newSnapshot;
534 535
}

536
WorkingCopy CppModelManager::buildWorkingCopyList()
con's avatar
con committed
537
{
538
    WorkingCopy workingCopy;
539

540 541
    foreach (const EditorDocumentHandle *cppEditor, cppEditors())
        workingCopy.insert(cppEditor->filePath(), cppEditor->contents(), cppEditor->revision());
con's avatar
con committed
542

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

549
    // Add the project configuration file
550
    QByteArray conf = codeModelConfiguration();
con's avatar
con committed
551
    conf += definedMacros();
552
    workingCopy.insert(configurationFileName(), conf);
con's avatar
con committed
553 554 555 556

    return workingCopy;
}

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

562 563 564 565 566
QByteArray CppModelManager::codeModelConfiguration() const
{
    return QByteArray::fromRawData(pp_configuration, qstrlen(pp_configuration));
}

567
QFuture<void> CppModelManager::updateSourceFiles(const QSet<QString> &sourceFiles,
568
                                                 ProgressNotificationMode mode)
569
{
570
    if (sourceFiles.isEmpty() || !d->m_indexerEnabled)
571 572
        return QFuture<void>();

573 574 575
    if (d->m_indexingSupporter)
        d->m_indexingSupporter->refreshSourceFiles(sourceFiles, mode);
    return d->m_internalIndexingSupport->refreshSourceFiles(sourceFiles, mode);
576
}
con's avatar
con committed
577

578
QList<ProjectInfo> CppModelManager::projectInfos() const
579
{
580 581
    QMutexLocker locker(&d->m_projectMutex);
    return d->m_projectToProjectsInfo.values();
582 583
}

584
ProjectInfo CppModelManager::projectInfo(ProjectExplorer::Project *project) const
585
{
586 587
    QMutexLocker locker(&d->m_projectMutex);
    return d->m_projectToProjectsInfo.value(project, ProjectInfo(project));
588 589
}

590 591 592 593 594 595
/// \brief Remove all files and their includes (recursively) of given ProjectInfo from the snapshot.
void CppModelManager::removeProjectInfoFilesAndIncludesFromSnapshot(const ProjectInfo &projectInfo)
{
    if (!projectInfo.isValid())
        return;

596
    QMutexLocker snapshotLocker(&d->m_snapshotMutex);
597 598
    foreach (const ProjectPart::Ptr &projectPart, projectInfo.projectParts()) {
        foreach (const ProjectFile &cxxFile, projectPart->files) {
599 600 601
            foreach (const QString &fileName, d->m_snapshot.allIncludesForDocument(cxxFile.path))
                d->m_snapshot.remove(fileName);
            d->m_snapshot.remove(cxxFile.path);
602 603 604 605
        }
    }
}

606
QList<EditorDocumentHandle *> CppModelManager::cppEditors() const
607
{
608 609
    QMutexLocker locker(&d->m_cppEditorsMutex);
    return d->m_cppEditors.values();
610 611
}

612 613 614
/// \brief Remove all given files from the snapshot.
void CppModelManager::removeFilesFromSnapshot(const QSet<QString> &filesToRemove)
{
615
    QMutexLocker snapshotLocker(&d->m_snapshotMutex);
616 617
    QSetIterator<QString> i(filesToRemove);
    while (i.hasNext())
618
        d->m_snapshot.remove(i.next());
619 620 621
}

class ProjectInfoComparer
622
{
623
public:
624 625
    ProjectInfoComparer(const ProjectInfo &oldProjectInfo,
                        const ProjectInfo &newProjectInfo)
626
        : m_old(oldProjectInfo)
627
        , m_oldSourceFiles(oldProjectInfo.sourceFiles())
628
        , m_new(newProjectInfo)
629
        , m_newSourceFiles(newProjectInfo.sourceFiles())
630 631 632 633 634 635 636 637 638 639
    {}

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

    bool configurationChanged() const
    {
        return definesChanged()
640
            || m_new.headerPaths() != m_old.headerPaths();
641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675
    }

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

676
        return CppModelManager::timeStampModifiedFiles(documentsToCheck);
677 678 679
    }

private:
680
    const ProjectInfo &m_old;
681 682
    const QSet<QString> m_oldSourceFiles;

683
    const ProjectInfo &m_new;
684 685 686
    const QSet<QString> m_newSourceFiles;
};

687 688 689
/// Make sure that m_projectMutex is locked when calling this.
void CppModelManager::recalculateFileToProjectParts()
{
690 691 692
    d->m_projectFileToProjectPart.clear();
    d->m_fileToProjectParts.clear();
    foreach (const ProjectInfo &projectInfo, d->m_projectToProjectsInfo) {
693
        foreach (const ProjectPart::Ptr &projectPart, projectInfo.projectParts()) {
694
            d->m_projectFileToProjectPart[projectPart->projectFile] = projectPart;
695
            foreach (const ProjectFile &cxxFile, projectPart->files)
696
                d->m_fileToProjectParts[cxxFile.path].append(projectPart);
697

698 699 700 701
        }
    }
}

702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719
void CppModelManager::updateVisibleEditorDocuments() const
{
    QSet<QString> visibleDocumentsInEditMode;
    foreach (Core::IEditor *editor, Core::EditorManager::visibleEditors()) {
        if (const Core::IDocument *document = editor->document()) {
            const QString filePath = document->filePath();
            QTC_ASSERT(!filePath.isEmpty(), continue);
            visibleDocumentsInEditMode.insert(filePath);
        }
    }

    // Re-process these documents
    foreach (const QString &filePath, visibleDocumentsInEditMode) {
        if (EditorDocumentHandle *editor = editorDocument(filePath))
            editor->processor()->run();
    }
}

720 721 722
QFuture<void> CppModelManager::updateProjectInfo(const ProjectInfo &newProjectInfo)
{
    if (!newProjectInfo.isValid())
723
        return QFuture<void>();
724

725
    QSet<QString> filesToReindex;
726 727
    bool filesRemoved = false;

728
    { // Only hold the mutex for a limited scope, so the dumping afterwards does not deadlock.
729
        QMutexLocker projectLocker(&d->m_projectMutex);
730 731

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

734
        // Check if we can avoid a full reindexing
735
        ProjectInfo oldProjectInfo = d->m_projectToProjectsInfo.value(project);
736
        if (oldProjectInfo.isValid()) {
737 738
            ProjectInfoComparer comparer(oldProjectInfo, newProjectInfo);
            if (comparer.nothingChanged())
739 740
                return QFuture<void>();

741 742 743
            // If the project configuration changed, do a full reindexing
            if (comparer.configurationChanged()) {
                removeProjectInfoFilesAndIncludesFromSnapshot(oldProjectInfo);
744
                filesToReindex.unite(newSourceFiles);
745 746 747

                // The "configuration file" includes all defines and therefore should be updated
                if (comparer.definesChanged()) {
748 749
                    QMutexLocker snapshotLocker(&d->m_snapshotMutex);
                    d->m_snapshot.remove(configurationFileName());
750
                }
751 752 753 754

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

                const QSet<QString> modifiedFiles = comparer.timeStampModifiedFiles(snapshot());
758
                filesToReindex.unite(modifiedFiles);
759
            }
760 761 762 763 764 765 766 767 768 769 770

            // 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 {
771
            filesToReindex.unite(newSourceFiles);
772 773
        }

774
        // Update Project/ProjectInfo and File/ProjectPart table
775 776
        d->m_dirty = true;
        d->m_projectToProjectsInfo.insert(project, newProjectInfo);
777
        recalculateFileToProjectParts();
778

779 780 781
    } // Mutex scope

    // If requested, dump everything we got
782
    if (DumpProjectInfo)
783
        dumpModelManagerConfiguration(QLatin1String("updateProjectInfo"));
784

785 786 787 788 789
    // Remove files from snapshot that are not reachable any more
    if (filesRemoved)
        GC();

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

791 792 793 794 795 796
    // Ideally, we would update all the editor documents that depend on the 'filesToReindex'.
    // However, on e.g. a session restore first the editor documents are created and then the
    // project updates come in. That is, there are no reasonable dependency tables based on
    // resolved includes that we could rely on.
    updateVisibleEditorDocuments();

797 798
    // Trigger reindexing
    return updateSourceFiles(filesToReindex, ForcedProgressNotification);
799 800
}

801 802
ProjectPart::Ptr CppModelManager::projectPartForProjectFile(const QString &projectFile) const
{
803
    return d->m_projectFileToProjectPart.value(projectFile);
804 805
}

806
QList<ProjectPart::Ptr> CppModelManager::projectPart(const QString &fileName) const
807
{
808 809
    QMutexLocker locker(&d->m_projectMutex);
    return d->m_fileToProjectParts.value(fileName);
810
}
811

812 813 814
QList<ProjectPart::Ptr> CppModelManager::projectPartFromDependencies(const QString &fileName) const
{
    QSet<ProjectPart::Ptr> parts;
815
    const QStringList deps = snapshot().filesDependingOn(fileName);
816 817 818 819 820 821

    {
        QMutexLocker locker(&d->m_projectMutex);
        foreach (const QString &dep, deps)
            parts.unite(QSet<ProjectPart::Ptr>::fromList(d->m_fileToProjectParts.value(dep)));
    }
822

823
    return parts.values();
824
}
con's avatar
con committed
825

826 827 828 829
ProjectPart::Ptr CppModelManager::fallbackProjectPart() const
{
    ProjectPart::Ptr part(new ProjectPart);

830 831
    part->projectDefines = d->m_definedMacros;
    part->headerPaths = d->m_headerPaths;
832 833
    part->languageVersion = ProjectPart::CXX14;
    part->languageExtensions = ProjectPart::AllExtensions;
834 835 836 837 838
    part->qtVersion = ProjectPart::Qt5;

    return part;
}

con's avatar
con committed
839 840
bool CppModelManager::isCppEditor(Core::IEditor *editor) const
{
841
    return editor->context().contains(ProjectExplorer::Constants::LANG_CXX);
con's avatar
con committed
842 843 844 845
}

void CppModelManager::emitDocumentUpdated(Document::Ptr doc)
{
846
    if (replaceDocument(doc))
847
        emit documentUpdated(doc);
Roberto Raggi's avatar
Roberto Raggi committed
848 849
}

850 851
void CppModelManager::onProjectAdded(ProjectExplorer::Project *)
{
852 853
    QMutexLocker locker(&d->m_projectMutex);
    d->m_dirty = true;
854 855
}

856 857
void CppModelManager::delayedGC()
{
858 859
    if (d->m_enableGC)
        d->m_delayedGcTimer.start(500);
860 861
}

con's avatar
con committed
862 863
void CppModelManager::onAboutToRemoveProject(ProjectExplorer::Project *project)
{
864
    do {
865 866 867
        QMutexLocker locker(&d->m_projectMutex);
        d->m_dirty = true;
        d->m_projectToProjectsInfo.remove(project);
868
        recalculateFileToProjectParts();
869 870
    } while (0);

871 872 873
    delayedGC();
}

874 875 876 877 878 879 880 881
void CppModelManager::onSourceFilesRefreshed() const
{
    if (BuiltinIndexingSupport::isFindErrorsIndexingActive()) {
        QTimer::singleShot(1, QCoreApplication::instance(), SLOT(quit()));
        qDebug("FindErrorsIndexing: Done, requesting Qt Creator to quit.");
    }
}

882 883
void CppModelManager::onAboutToLoadSession()
{
884 885
    if (d->m_delayedGcTimer.isActive())
        d->m_delayedGcTimer.stop();
con's avatar
con committed
886 887 888
    GC();
}

889
void CppModelManager::onAboutToUnloadSession()
con's avatar
con committed
890
{
891
    Core::ProgressManager::cancelTasks(CppTools::Constants::TASK_INDEX);
892
    do {
hjk's avatar