cppmodelmanager.cpp 41.8 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
Eike Ziller's avatar
Eike Ziller committed
3 4
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
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
** 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
Eike Ziller's avatar
Eike Ziller committed
12 13
** a written agreement between you and The Qt Company.  For licensing terms and
** conditions see http://www.qt.io/terms-conditions.  For further information
Eike Ziller's avatar
Eike Ziller committed
14
** 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
**
Eike Ziller's avatar
Eike Ziller committed
25 26
** In addition, as a special exception, The Qt Company gives you certain additional
** rights.  These rights are described in The Qt Company 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 "baseeditordocumentprocessor.h"
35
#include "builtinindexingsupport.h"
36
#include "cppcodemodelinspectordumper.h"
37
#include "cppcodemodelsettings.h"
38
#include "cppfindreferences.h"
39
#include "cppindexingsupport.h"
40
#include "cppmodelmanagersupportinternal.h"
41
#include "cpprefactoringchanges.h"
42
#include "cppsourceprocessor.h"
43
#include "cpptoolsconstants.h"
44
#include "cpptoolsplugin.h"
45
#include "cpptoolsreuse.h"
46
#include "editordocumenthandle.h"
47
#include "symbolfinder.h"
con's avatar
con committed
48

49
#include <coreplugin/documentmanager.h>
50 51
#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h>
52 53
#include <coreplugin/editormanager/editormanager.h>
#include <texteditor/textdocument.h>
54
#include <projectexplorer/project.h>
55 56
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/session.h>
57
#include <extensionsystem/pluginmanager.h>
58
#include <utils/algorithm.h>
59
#include <utils/fileutils.h>
hjk's avatar
hjk committed
60
#include <utils/qtcassert.h>
con's avatar
con committed
61

62 63
#include <QCoreApplication>
#include <QDebug>
64
#include <QDir>
65 66
#include <QMutexLocker>
#include <QTextBlock>
67
#include <QTimer>
68

69 70
#if defined(QTCREATOR_WITH_DUMP_AST) && defined(Q_CC_GNU)
#define WITH_AST_DUMP
71 72
#include <iostream>
#include <sstream>
73
#endif
hjk's avatar
hjk committed
74

75 76
Q_DECLARE_METATYPE(QSet<QString>)

77 78
static const bool DumpProjectInfo = qgetenv("QTC_DUMP_PROJECT_INFO") == "1";

79 80
using namespace CppTools;
using namespace CppTools::Internal;
con's avatar
con committed
81 82
using namespace CPlusPlus;

83
#ifdef QTCREATOR_WITH_DUMP_AST
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 114 115 116 117 118 119 120 121 122

#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
123
#endif // QTCREATOR_WITH_DUMP_AST
124

125 126 127 128 129 130 131 132 133 134 135
namespace CppTools {
namespace Internal {

static QMutex m_instanceMutex;
static CppModelManager *m_instance;

class CppModelManagerPrivate
{
public:
    // Snapshot
    mutable QMutex m_snapshotMutex;
136
    Snapshot m_snapshot;
137 138 139 140

    // Project integration
    mutable QMutex m_projectMutex;
    QMap<ProjectExplorer::Project *, ProjectInfo> m_projectToProjectsInfo;
141
    QMap<Utils::FileName, QList<ProjectPart::Ptr> > m_fileToProjectParts;
142
    QMap<QString, ProjectPart::Ptr> m_projectPartIdToProjectProjectPart;
143 144 145 146 147 148 149
    // 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
150 151
    mutable QMutex m_cppEditorDocumentsMutex;
    QMap<QString, CppEditorDocumentHandle *> m_cppEditorDocuments;
152 153 154
    QSet<AbstractEditorSupport *> m_extraEditorSupports;

    // Completion & highlighting
155 156 157 158
    ModelManagerSupportProviderInternal m_modelManagerSupportInternalProvider;
    ModelManagerSupport::Ptr m_modelManagerSupportInternal;
    QHash<QString, ModelManagerSupportProvider *> m_availableModelManagerSupports;
    QHash<QString, ModelManagerSupport::Ptr> m_activeModelManagerSupports;
159 160 161 162 163 164 165 166

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

    CppFindReferences *m_findReferences;

167 168
    SymbolFinder m_symbolFinder;

169 170 171 172 173 174 175
    bool m_enableGC;
    QTimer m_delayedGcTimer;
};

} // namespace Internal

const char pp_configuration[] =
con's avatar
con committed
176
    "# 1 \"<configuration>\"\n"
177
    "#define Q_CREATOR_RUN 1\n"
con's avatar
con committed
178 179 180 181 182 183
    "#define __cplusplus 1\n"
    "#define __extension__\n"
    "#define __context__\n"
    "#define __range__\n"
    "#define   restrict\n"
    "#define __restrict\n"
184
    "#define __restrict__\n"
con's avatar
con committed
185

186 187 188 189
    "#define __complex__\n"
    "#define __imag__\n"
    "#define __real__\n"

190 191
    "#define __builtin_va_arg(a,b) ((b)0)\n"

192 193
    "#define _Pragma(x)\n" // C99 _Pragma operator

con's avatar
con committed
194 195
    // ### add macros for win32
    "#define __cdecl\n"
196
    "#define __stdcall\n"
197
    "#define __thiscall\n"
con's avatar
con committed
198 199 200 201 202
    "#define QT_WA(x) x\n"
    "#define CALLBACK\n"
    "#define STDMETHODCALLTYPE\n"
    "#define __RPC_FAR\n"
    "#define __declspec(a)\n"
203 204 205
    "#define STDMETHOD(method) virtual HRESULT STDMETHODCALLTYPE method\n"
    "#define __try try\n"
    "#define __except catch\n"
206 207
    "#define __finally\n"
    "#define __inline inline\n"
208 209
    "#define __forceinline inline\n"
    "#define __pragma(x)\n";
con's avatar
con committed
210

211
QSet<QString> CppModelManager::timeStampModifiedFiles(const QList<Document::Ptr> &documentsToCheck)
212
{
213
    QSet<QString> sourceFiles;
214

215
    foreach (const Document::Ptr doc, documentsToCheck) {
216 217
        const QDateTime lastModified = doc->lastModified();

218
        if (!lastModified.isNull()) {
219 220 221
            QFileInfo fileInfo(doc->fileName());

            if (fileInfo.exists() && fileInfo.lastModified() != lastModified)
222
                sourceFiles.insert(doc->fileName());
223 224 225
        }
    }

226 227 228
    return sourceFiles;
}

229 230 231 232 233 234 235 236 237 238 239 240
/*!
 * \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
241
    return new CppSourceProcessor(that->snapshot(), [that](const Document::Ptr &doc) {
242 243 244 245 246
        const Document::Ptr previousDocument = that->document(doc->fileName());
        const unsigned newRevision = previousDocument.isNull()
                ? 1U
                : previousDocument->revision() + 1;
        doc->setRevision(newRevision);
247 248 249 250 251
        that->emitDocumentUpdated(doc);
        doc->releaseSourceAndAST();
    });
}

252 253 254 255 256 257 258
QString CppModelManager::editorConfigurationFileName()
{
    return QLatin1String("<per-editor-defines>");
}

QString CppModelManager::configurationFileName()
{
259
    return Preprocessor::configurationFileName();
260 261
}

262 263 264 265 266 267 268
void CppModelManager::updateModifiedSourceFiles()
{
    const Snapshot snapshot = this->snapshot();
    QList<Document::Ptr> documentsToCheck;
    foreach (const Document::Ptr document, snapshot)
        documentsToCheck << document;

269
    updateSourceFiles(timeStampModifiedFiles(documentsToCheck));
270 271
}

con's avatar
con committed
272 273
/*!
    \class CppTools::CppModelManager
274 275 276
    \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
277

278 279 280 281
    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
282 283
*/

284 285
CppModelManager *CppModelManager::instance()
{
286 287 288 289 290 291 292 293
    if (m_instance)
        return m_instance;

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

    return m_instance;
294 295
}

296
CppModelManager::CppModelManager(QObject *parent)
297
    : CppModelManagerBase(parent), d(new CppModelManagerPrivate)
con's avatar
con committed
298
{
299 300 301
    d->m_indexingSupporter = 0;
    d->m_enableGC = true;

302
    qRegisterMetaType<QSet<QString> >();
303
    connect(this, SIGNAL(sourceFilesRefreshed(QSet<QString>)),
304
            this, SLOT(onSourceFilesRefreshed()));
Erik Verbruggen's avatar
Erik Verbruggen committed
305

306 307
    d->m_findReferences = new CppFindReferences(this);
    d->m_indexerEnabled = qgetenv("QTC_NO_CODE_INDEXER") != "1";
Roberto Raggi's avatar
Roberto Raggi committed
308

309
    d->m_dirty = true;
310

311 312 313
    d->m_delayedGcTimer.setObjectName(QLatin1String("CppModelManager::m_delayedGcTimer"));
    d->m_delayedGcTimer.setSingleShot(true);
    connect(&d->m_delayedGcTimer, SIGNAL(timeout()), this, SLOT(GC()));
314

hjk's avatar
hjk committed
315 316
    QObject *sessionManager = ProjectExplorer::SessionManager::instance();
    connect(sessionManager, SIGNAL(projectAdded(ProjectExplorer::Project*)),
317
            this, SLOT(onProjectAdded(ProjectExplorer::Project*)));
hjk's avatar
hjk committed
318
    connect(sessionManager, SIGNAL(aboutToRemoveProject(ProjectExplorer::Project*)),
Robert Loehning's avatar
Robert Loehning committed
319
            this, SLOT(onAboutToRemoveProject(ProjectExplorer::Project*)));
hjk's avatar
hjk committed
320
    connect(sessionManager, SIGNAL(aboutToLoadSession(QString)),
321
            this, SLOT(onAboutToLoadSession()));
hjk's avatar
hjk committed
322
    connect(sessionManager, SIGNAL(aboutToUnloadSession(QString)),
323
            this, SLOT(onAboutToUnloadSession()));
324 325 326 327

    connect(Core::EditorManager::instance(), &Core::EditorManager::currentEditorChanged,
            this, &CppModelManager::onCurrentEditorChanged);

328 329
    connect(Core::DocumentManager::instance(), &Core::DocumentManager::allDocumentsRenamed,
            this, &CppModelManager::renameIncludes);
con's avatar
con committed
330

331 332 333
    connect(Core::ICore::instance(), SIGNAL(coreAboutToClose()),
            this, SLOT(onCoreAboutToClose()));

con's avatar
con committed
334
    qRegisterMetaType<CPlusPlus::Document::Ptr>("CPlusPlus::Document::Ptr");
335
    qRegisterMetaType<QList<Document::DiagnosticMessage>>(
336
                "QList<CPlusPlus::Document::DiagnosticMessage>");
con's avatar
con committed
337

338 339 340 341 342 343 344 345 346 347 348
    QSharedPointer<CppCodeModelSettings> codeModelSettings
            = CppToolsPlugin::instance()->codeModelSettings();
    codeModelSettings->setDefaultId(d->m_modelManagerSupportInternalProvider.id());
    connect(codeModelSettings.data(), &CppCodeModelSettings::changed,
            this, &CppModelManager::onCodeModelSettingsChanged);

    d->m_modelManagerSupportInternal
            = d->m_modelManagerSupportInternalProvider.createModelManagerSupport();
    d->m_activeModelManagerSupports.insert(d->m_modelManagerSupportInternalProvider.id(),
                                           d->m_modelManagerSupportInternal);
    addModelManagerSupportProvider(&d->m_modelManagerSupportInternalProvider);
349

350
    d->m_internalIndexingSupport = new BuiltinIndexingSupport;
con's avatar
con committed
351 352 353
}

CppModelManager::~CppModelManager()
354
{
355
    delete d->m_internalIndexingSupport;
356
    delete d;
357
}
con's avatar
con committed
358

359
Snapshot CppModelManager::snapshot() const
360
{
361 362
    QMutexLocker locker(&d->m_snapshotMutex);
    return d->m_snapshot;
363
}
con's avatar
con committed
364

365 366
Document::Ptr CppModelManager::document(const QString &fileName) const
{
367 368
    QMutexLocker locker(&d->m_snapshotMutex);
    return d->m_snapshot.document(fileName);
369 370 371 372 373 374 375
}

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

378
    Document::Ptr previous = d->m_snapshot.document(newDoc->fileName());
379 380 381 382
    if (previous && (newDoc->revision() != 0 && newDoc->revision() < previous->revision()))
        // the new document is outdated
        return false;

383
    d->m_snapshot.insert(newDoc);
384 385 386
    return true;
}

387 388
void CppModelManager::ensureUpdated()
{
389 390
    QMutexLocker locker(&d->m_projectMutex);
    if (!d->m_dirty)
391 392
        return;

393 394 395 396
    d->m_projectFiles = internalProjectFiles();
    d->m_headerPaths = internalHeaderPaths();
    d->m_definedMacros = internalDefinedMacros();
    d->m_dirty = false;
397 398
}

399
QStringList CppModelManager::internalProjectFiles() const
con's avatar
con committed
400 401
{
    QStringList files;
402
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(d->m_projectToProjectsInfo);
con's avatar
con committed
403 404
    while (it.hasNext()) {
        it.next();
405
        const ProjectInfo pinfo = it.value();
406
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
407 408
            foreach (const ProjectFile &file, part->files)
                files += file.path;
409
        }
con's avatar
con committed
410
    }
411
    files.removeDuplicates();
con's avatar
con committed
412 413 414
    return files;
}

415
ProjectPart::HeaderPaths CppModelManager::internalHeaderPaths() const
con's avatar
con committed
416
{
417
    ProjectPart::HeaderPaths headerPaths;
418
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(d->m_projectToProjectsInfo);
con's avatar
con committed
419 420
    while (it.hasNext()) {
        it.next();
421
        const ProjectInfo pinfo = it.value();
422 423
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
            foreach (const ProjectPart::HeaderPath &path, part->headerPaths) {
424
                const ProjectPart::HeaderPath hp(QDir::cleanPath(path.path), path.type);
425 426 427 428
                if (!headerPaths.contains(hp))
                    headerPaths += hp;
            }
        }
con's avatar
con committed
429
    }
430
    return headerPaths;
con's avatar
con committed
431 432
}

433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448
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);
        }
    }
}

449
QByteArray CppModelManager::internalDefinedMacros() const
con's avatar
con committed
450 451
{
    QByteArray macros;
452
    QSet<QByteArray> alreadyIn;
453
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(d->m_projectToProjectsInfo);
con's avatar
con committed
454 455
    while (it.hasNext()) {
        it.next();
456
        const ProjectInfo pinfo = it.value();
457
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
458 459
            addUnique(part->toolchainDefines.split('\n'), &macros, &alreadyIn);
            addUnique(part->projectDefines.split('\n'), &macros, &alreadyIn);
460
            if (!part->projectConfigFile.isEmpty())
461
                macros += ProjectPart::readProjectConfigFile(part);
462
        }
con's avatar
con committed
463 464 465 466
    }
    return macros;
}

467
/// This function will acquire mutexes!
468
void CppModelManager::dumpModelManagerConfiguration(const QString &logFileId)
469
{
470 471 472 473
    const Snapshot globalSnapshot = snapshot();
    const QString globalSnapshotTitle
        = QString::fromLatin1("Global/Indexing Snapshot (%1 Documents)").arg(globalSnapshot.size());

474
    CppCodeModelInspector::Dumper dumper(globalSnapshot, logFileId);
475 476 477
    dumper.dumpProjectInfos(projectInfos());
    dumper.dumpSnapshot(globalSnapshot, globalSnapshotTitle, /*isGlobalSnapshot=*/ true);
    dumper.dumpWorkingCopy(workingCopy());
478
    ensureUpdated();
479
    dumper.dumpMergedEntities(d->m_headerPaths, d->m_definedMacros);
480 481
}

482 483 484 485 486
QSet<AbstractEditorSupport *> CppModelManager::abstractEditorSupports() const
{
    return d->m_extraEditorSupports;
}

487
void CppModelManager::addExtraEditorSupport(AbstractEditorSupport *editorSupport)
488
{
489
    d->m_extraEditorSupports.insert(editorSupport);
490 491
}

492
void CppModelManager::removeExtraEditorSupport(AbstractEditorSupport *editorSupport)
493
{
494
    d->m_extraEditorSupports.remove(editorSupport);
495 496
}

497
CppEditorDocumentHandle *CppModelManager::cppEditorDocument(const QString &filePath) const
498
{
499 500
    if (filePath.isEmpty())
        return 0;
501

502 503
    QMutexLocker locker(&d->m_cppEditorDocumentsMutex);
    return d->m_cppEditorDocuments.value(filePath, 0);
504 505
}

506
void CppModelManager::registerCppEditorDocument(CppEditorDocumentHandle *editorDocument)
507
{
508 509 510
    QTC_ASSERT(editorDocument, return);
    const QString filePath = editorDocument->filePath();
    QTC_ASSERT(!filePath.isEmpty(), return);
511

512 513 514
    QMutexLocker locker(&d->m_cppEditorDocumentsMutex);
    QTC_ASSERT(d->m_cppEditorDocuments.value(filePath, 0) == 0, return);
    d->m_cppEditorDocuments.insert(filePath, editorDocument);
515
}
516

517
void CppModelManager::unregisterCppEditorDocument(const QString &filePath)
518 519
{
    QTC_ASSERT(!filePath.isEmpty(), return);
520

521 522
    static short closedCppDocuments = 0;
    int openCppDocuments = 0;
523

524
    {
525 526 527 528
        QMutexLocker locker(&d->m_cppEditorDocumentsMutex);
        QTC_ASSERT(d->m_cppEditorDocuments.value(filePath, 0), return);
        QTC_CHECK(d->m_cppEditorDocuments.remove(filePath) == 1);
        openCppDocuments = d->m_cppEditorDocuments.size();
529 530
    }

531 532 533
    ++closedCppDocuments;
    if (openCppDocuments == 0 || closedCppDocuments == 5) {
        closedCppDocuments = 0;
534
        delayedGC();
535 536 537
    }
}

538
QList<int> CppModelManager::references(Symbol *symbol, const LookupContext &context)
539
{
540
    return d->m_findReferences->references(symbol, context);
541 542
}

543
void CppModelManager::findUsages(Symbol *symbol, const LookupContext &context)
Roberto Raggi's avatar
Roberto Raggi committed
544
{
545
    if (symbol->identifier())
546
        d->m_findReferences->findUsages(symbol, context);
547 548
}

549 550
void CppModelManager::renameUsages(Symbol *symbol,
                                   const LookupContext &context,
551
                                   const QString &replacement)
552 553
{
    if (symbol->identifier())
554
        d->m_findReferences->renameUsages(symbol, context, replacement);
Roberto Raggi's avatar
Roberto Raggi committed
555 556
}

557
void CppModelManager::findMacroUsages(const Macro &macro)
Christian Kamm's avatar
Christian Kamm committed
558
{
559
    d->m_findReferences->findMacroUses(macro);
Christian Kamm's avatar
Christian Kamm committed
560 561
}

562
void CppModelManager::renameMacroUsages(const Macro &macro, const QString &replacement)
563
{
564
    d->m_findReferences->renameMacroUses(macro, replacement);
565 566
}

567
void CppModelManager::replaceSnapshot(const Snapshot &newSnapshot)
568
{
569 570
    QMutexLocker snapshotLocker(&d->m_snapshotMutex);
    d->m_snapshot = newSnapshot;
571 572
}

573
WorkingCopy CppModelManager::buildWorkingCopyList()
con's avatar
con committed
574
{
575
    WorkingCopy workingCopy;
576

577 578 579 580 581
    foreach (const CppEditorDocumentHandle *cppEditorDocument, cppEditorDocuments()) {
        workingCopy.insert(cppEditorDocument->filePath(),
                           cppEditorDocument->contents(),
                           cppEditorDocument->revision());
    }
con's avatar
con committed
582

583
    QSetIterator<AbstractEditorSupport *> it(d->m_extraEditorSupports);
584 585
    while (it.hasNext()) {
        AbstractEditorSupport *es = it.next();
586
        workingCopy.insert(es->fileName(), es->contents(), es->revision());
587 588
    }

589
    // Add the project configuration file
590
    QByteArray conf = codeModelConfiguration();
con's avatar
con committed
591
    conf += definedMacros();
592
    workingCopy.insert(configurationFileName(), conf);
con's avatar
con committed
593 594 595 596

    return workingCopy;
}

597
WorkingCopy CppModelManager::workingCopy() const
598 599 600 601
{
    return const_cast<CppModelManager *>(this)->buildWorkingCopyList();
}

602 603 604 605 606
QByteArray CppModelManager::codeModelConfiguration() const
{
    return QByteArray::fromRawData(pp_configuration, qstrlen(pp_configuration));
}

607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627
static QSet<QString> tooBigFilesRemoved(const QSet<QString> &files, int fileSizeLimit)
{
    if (fileSizeLimit == 0)
        return files;

    QSet<QString> result;
    QFileInfo fileInfo;

    QSetIterator<QString> i(files);
    while (i.hasNext()) {
        const QString filePath = i.next();
        fileInfo.setFile(filePath);
        if (skipFileDueToSizeLimit(fileInfo), fileSizeLimit)
            continue;

        result << filePath;
    }

    return result;
}

628
QFuture<void> CppModelManager::updateSourceFiles(const QSet<QString> &sourceFiles,
629
                                                 ProgressNotificationMode mode)
630
{
631
    if (sourceFiles.isEmpty() || !d->m_indexerEnabled)
632 633
        return QFuture<void>();

634 635
    const auto filteredFiles = tooBigFilesRemoved(sourceFiles, fileSizeLimit());

636
    if (d->m_indexingSupporter)
637 638
        d->m_indexingSupporter->refreshSourceFiles(filteredFiles, mode);
    return d->m_internalIndexingSupport->refreshSourceFiles(filteredFiles, mode);
639
}
con's avatar
con committed
640

641
QList<ProjectInfo> CppModelManager::projectInfos() const
642
{
643 644
    QMutexLocker locker(&d->m_projectMutex);
    return d->m_projectToProjectsInfo.values();
645 646
}

647
ProjectInfo CppModelManager::projectInfo(ProjectExplorer::Project *project) const
648
{
649
    QMutexLocker locker(&d->m_projectMutex);
650
    return d->m_projectToProjectsInfo.value(project, ProjectInfo());
651 652
}

653 654 655 656 657 658
/// \brief Remove all files and their includes (recursively) of given ProjectInfo from the snapshot.
void CppModelManager::removeProjectInfoFilesAndIncludesFromSnapshot(const ProjectInfo &projectInfo)
{
    if (!projectInfo.isValid())
        return;

659
    QMutexLocker snapshotLocker(&d->m_snapshotMutex);
660 661
    foreach (const ProjectPart::Ptr &projectPart, projectInfo.projectParts()) {
        foreach (const ProjectFile &cxxFile, projectPart->files) {
662 663 664
            foreach (const QString &fileName, d->m_snapshot.allIncludesForDocument(cxxFile.path))
                d->m_snapshot.remove(fileName);
            d->m_snapshot.remove(cxxFile.path);
665 666 667 668
        }
    }
}

669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701
void CppModelManager::handleAddedModelManagerSupports(const QSet<QString> &supportIds)
{
    foreach (const QString &id, supportIds) {
        ModelManagerSupportProvider * const provider = d->m_availableModelManagerSupports.value(id);
        if (provider) {
            QTC_CHECK(!d->m_activeModelManagerSupports.contains(id));
            d->m_activeModelManagerSupports.insert(id, provider->createModelManagerSupport());
        }
    }
}

QList<ModelManagerSupport::Ptr> CppModelManager::handleRemovedModelManagerSupports(
        const QSet<QString> &supportIds)
{
    QList<ModelManagerSupport::Ptr> removed;

    foreach (const QString &id, supportIds) {
        const ModelManagerSupport::Ptr support = d->m_activeModelManagerSupports.value(id);
        d->m_activeModelManagerSupports.remove(id);
        removed << support;
    }

    return removed;
}

void CppModelManager::closeCppEditorDocuments()
{
    QList<Core::IDocument *> cppDocumentsToClose;
    foreach (CppEditorDocumentHandle *cppDocument, cppEditorDocuments())
        cppDocumentsToClose << cppDocument->processor()->baseTextDocument();
    QTC_CHECK(Core::EditorManager::closeDocuments(cppDocumentsToClose));
}

702
QList<CppEditorDocumentHandle *> CppModelManager::cppEditorDocuments() const
703
{
704 705
    QMutexLocker locker(&d->m_cppEditorDocumentsMutex);
    return d->m_cppEditorDocuments.values();
706 707
}

708 709 710
/// \brief Remove all given files from the snapshot.
void CppModelManager::removeFilesFromSnapshot(const QSet<QString> &filesToRemove)
{
711
    QMutexLocker snapshotLocker(&d->m_snapshotMutex);
712 713
    QSetIterator<QString> i(filesToRemove);
    while (i.hasNext())
714
        d->m_snapshot.remove(i.next());
715 716
}

717
static QSet<QString> projectPartIds(const QSet<ProjectPart::Ptr> &projectParts)
718
{
719 720 721
    return Utils::transform(projectParts, [](const ProjectPart::Ptr &projectPart) {
        return projectPart->id();
    });
722 723
}

724
class ProjectInfoComparer
725
{
726
public:
727 728
    ProjectInfoComparer(const ProjectInfo &oldProjectInfo,
                        const ProjectInfo &newProjectInfo)
729
        : m_old(oldProjectInfo)
730
        , m_oldSourceFiles(oldProjectInfo.sourceFiles())
731
        , m_new(newProjectInfo)
732
        , m_newSourceFiles(newProjectInfo.sourceFiles())
733 734
    {}

735 736 737
    bool definesChanged() const { return m_new.definesChanged(m_old); }
    bool configurationChanged() const { return m_new.configurationChanged(m_old); }
    bool configurationOrFilesChanged() const { return m_new.configurationOrFilesChanged(m_old); }
738 739 740 741 742 743 744 745 746 747 748 749 750 751 752

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

753 754
    QStringList removedProjectParts()
    {
755 756 757
        QSet<QString> removed = projectPartIds(m_old.projectParts().toSet());
        removed.subtract(projectPartIds(m_new.projectParts().toSet()));
        return removed.toList();
758 759
    }

760 761 762 763 764 765 766 767 768 769 770 771 772 773
    /// 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;
        }

774
        return CppModelManager::timeStampModifiedFiles(documentsToCheck);
775 776 777
    }

private:
778
    const ProjectInfo &m_old;
779 780
    const QSet<QString> m_oldSourceFiles;

781
    const ProjectInfo &m_new;
782 783 784
    const QSet<QString> m_newSourceFiles;
};

785
/// Make sure that m_projectMutex is locked when calling this.
786
void CppModelManager::recalculateProjectPartMappings()
787
{
788
    d->m_projectPartIdToProjectProjectPart.clear();
789 790
    d->m_fileToProjectParts.clear();
    foreach (const ProjectInfo &projectInfo, d->m_projectToProjectsInfo) {
791
        foreach (const ProjectPart::Ptr &projectPart, projectInfo.projectParts()) {
792
            d->m_projectPartIdToProjectProjectPart[projectPart->id()] = projectPart;
793
            foreach (const ProjectFile &cxxFile, projectPart->files)
794 795
                d->m_fileToProjectParts[Utils::FileName::fromString(cxxFile.path)].append(
                            projectPart);
796

797 798 799 800
        }
    }
}

801
void CppModelManager::updateCppEditorDocuments() const
802
{
803 804
    // Refresh visible documents
    QSet<Core::IDocument *> visibleCppEditorDocuments;
805
    foreach (Core::IEditor *editor, Core::EditorManager::visibleEditors()) {
806
        if (Core::IDocument *document = editor->document()) {
807 808
            const QString filePath = document->filePath().toString();
            if (CppEditorDocumentHandle *theCppEditorDocument = cppEditorDocument(filePath)) {
809
                visibleCppEditorDocuments.insert(document);
810
                theCppEditorDocument->processor()->run();
811
            }
812 813 814
        }
    }

815 816 817 818 819
    // Mark invisible documents dirty
    QSet<Core::IDocument *> invisibleCppEditorDocuments
        = Core::DocumentModel::openedDocuments().toSet();
    invisibleCppEditorDocuments.subtract(visibleCppEditorDocuments);
    foreach (Core::IDocument *document, invisibleCppEditorDocuments) {
820 821 822
        const QString filePath = document->filePath().toString();
        if (CppEditorDocumentHandle *theCppEditorDocument = cppEditorDocument(filePath))
            theCppEditorDocument->setNeedsRefresh(true);
823 824 825
    }
}

826 827 828
QFuture<void> CppModelManager::updateProjectInfo(const ProjectInfo &newProjectInfo)
{
    if (!newProjectInfo.isValid())
829
        return QFuture<void>();
830

831
    QSet<QString> filesToReindex;
832 833
    bool filesRemoved = false;

834
    { // Only hold the mutex for a limited scope, so the dumping afterwards does not deadlock.
835
        QMutexLocker projectLocker(&d->m_projectMutex);
836 837

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

840
        // Check if we can avoid a full reindexing
841
        ProjectInfo oldProjectInfo = d->m_projectToProjectsInfo.value(project);
842
        if (oldProjectInfo.isValid()) {
843
            ProjectInfoComparer comparer(oldProjectInfo, newProjectInfo);
844

845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865
            if (comparer.configurationOrFilesChanged()) {
                d->m_dirty = true;

                // If the project configuration changed, do a full reindexing
                if (comparer.configurationChanged()) {
                    removeProjectInfoFilesAndIncludesFromSnapshot(oldProjectInfo);
                    filesToReindex.unite(newSourceFiles);

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

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

                    const QSet<QString> modifiedFiles = comparer.timeStampModifiedFiles(snapshot());
                    filesToReindex.unite(modifiedFiles);
866
                }
867

868 869 870 871 872 873 874
                // 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);
                }
875 876
            }

877 878 879
            // Announce removed project parts
            emit projectPartsRemoved(comparer.removedProjectParts());

880 881
        // A new project was opened/created, do a full indexing
        } else {
882
            d->m_dirty = true;
883
            filesToReindex.unite(newSourceFiles);
884 885
        }

886
        // Update Project/ProjectInfo and File/ProjectPart table
887
        d->m_projectToProjectsInfo.insert(project, newProjectInfo);
888
        recalculateProjectPartMappings();
889

890 891 892
    } // Mutex scope

    // If requested, dump everything we got
893
    if (DumpProjectInfo)
894
        dumpModelManagerConfiguration(QLatin1String("updateProjectInfo"));
895

896 897 898 899
    // Remove files from snapshot that are not reachable any more
    if (filesRemoved)
        GC();

900
    // Announce added project parts
901
    emit projectPartsUpdated(newProjectInfo.project().data());