cppmodelmanager.cpp 39.7 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
    QSet<AbstractEditorSupport *> m_extraEditorSupports;

154 155 156 157
    // Model Manager Supports for e.g. completion and highlighting
    ModelManagerSupportProvider *m_clangModelManagerSupportProvider;
    ModelManagerSupport::Ptr m_builtinModelManagerSupport;
    ModelManagerSupport::Ptr m_activeModelManagerSupport;
158 159 160 161 162 163 164 165

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

    CppFindReferences *m_findReferences;

166 167
    SymbolFinder m_symbolFinder;

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

} // namespace Internal

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

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

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

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

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

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

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

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

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

225 226 227
    return sourceFiles;
}

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

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

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

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

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

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

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

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

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

    return m_instance;
293 294
}

295 296 297 298 299 300 301 302
void CppModelManager::initializeModelManagerSupports()
{
    d->m_clangModelManagerSupportProvider = nullptr;
    d->m_builtinModelManagerSupport
            = ModelManagerSupportProviderInternal().createModelManagerSupport();
    d->m_activeModelManagerSupport = d->m_builtinModelManagerSupport;
}

303
CppModelManager::CppModelManager(QObject *parent)
304
    : CppModelManagerBase(parent), d(new CppModelManagerPrivate)
con's avatar
con committed
305
{
306 307 308
    d->m_indexingSupporter = 0;
    d->m_enableGC = true;

309
    qRegisterMetaType<QSet<QString> >();
310
    connect(this, SIGNAL(sourceFilesRefreshed(QSet<QString>)),
311
            this, SLOT(onSourceFilesRefreshed()));
Erik Verbruggen's avatar
Erik Verbruggen committed
312

313 314
    d->m_findReferences = new CppFindReferences(this);
    d->m_indexerEnabled = qgetenv("QTC_NO_CODE_INDEXER") != "1";
Roberto Raggi's avatar
Roberto Raggi committed
315

316
    d->m_dirty = true;
317

318 319 320
    d->m_delayedGcTimer.setObjectName(QLatin1String("CppModelManager::m_delayedGcTimer"));
    d->m_delayedGcTimer.setSingleShot(true);
    connect(&d->m_delayedGcTimer, SIGNAL(timeout()), this, SLOT(GC()));
321

hjk's avatar
hjk committed
322 323
    QObject *sessionManager = ProjectExplorer::SessionManager::instance();
    connect(sessionManager, SIGNAL(projectAdded(ProjectExplorer::Project*)),
324
            this, SLOT(onProjectAdded(ProjectExplorer::Project*)));
hjk's avatar
hjk committed
325
    connect(sessionManager, SIGNAL(aboutToRemoveProject(ProjectExplorer::Project*)),
Robert Loehning's avatar
Robert Loehning committed
326
            this, SLOT(onAboutToRemoveProject(ProjectExplorer::Project*)));
hjk's avatar
hjk committed
327
    connect(sessionManager, SIGNAL(aboutToLoadSession(QString)),
328
            this, SLOT(onAboutToLoadSession()));
hjk's avatar
hjk committed
329
    connect(sessionManager, SIGNAL(aboutToUnloadSession(QString)),
330
            this, SLOT(onAboutToUnloadSession()));
331 332 333 334

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

335 336
    connect(Core::DocumentManager::instance(), &Core::DocumentManager::allDocumentsRenamed,
            this, &CppModelManager::renameIncludes);
con's avatar
con committed
337

338 339 340
    connect(Core::ICore::instance(), SIGNAL(coreAboutToClose()),
            this, SLOT(onCoreAboutToClose()));

con's avatar
con committed
341
    qRegisterMetaType<CPlusPlus::Document::Ptr>("CPlusPlus::Document::Ptr");
342
    qRegisterMetaType<QList<Document::DiagnosticMessage>>(
343
                "QList<CPlusPlus::Document::DiagnosticMessage>");
con's avatar
con committed
344

345 346 347 348 349
    QSharedPointer<CppCodeModelSettings> codeModelSettings
            = CppToolsPlugin::instance()->codeModelSettings();
    connect(codeModelSettings.data(), &CppCodeModelSettings::changed,
            this, &CppModelManager::onCodeModelSettingsChanged);

350
    initializeModelManagerSupports();
351

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

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

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

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

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

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

385
    d->m_snapshot.insert(newDoc);
386 387 388
    return true;
}

389
/// Make sure that m_projectMutex is locked when calling this.
390 391
void CppModelManager::ensureUpdated()
{
392
    if (!d->m_dirty)
393 394
        return;

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

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

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

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

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

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

476
    CppCodeModelInspector::Dumper dumper(globalSnapshot, logFileId);
477 478 479
    dumper.dumpProjectInfos(projectInfos());
    dumper.dumpSnapshot(globalSnapshot, globalSnapshotTitle, /*isGlobalSnapshot=*/ true);
    dumper.dumpWorkingCopy(workingCopy());
480
    dumper.dumpMergedEntities(headerPaths(), definedMacros());
481 482
}

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

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

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

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

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

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

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

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

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

525
    {
526 527 528 529
        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();
530 531
    }

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

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

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

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

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

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

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

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

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

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

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

    return workingCopy;
}

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

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

608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628
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;
}

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

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

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

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

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

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

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

670 671 672 673 674 675 676 677
void CppModelManager::closeCppEditorDocuments()
{
    QList<Core::IDocument *> cppDocumentsToClose;
    foreach (CppEditorDocumentHandle *cppDocument, cppEditorDocuments())
        cppDocumentsToClose << cppDocument->processor()->baseTextDocument();
    QTC_CHECK(Core::EditorManager::closeDocuments(cppDocumentsToClose));
}

678
QList<CppEditorDocumentHandle *> CppModelManager::cppEditorDocuments() const
679
{
680 681
    QMutexLocker locker(&d->m_cppEditorDocumentsMutex);
    return d->m_cppEditorDocuments.values();
682 683
}

684 685 686
/// \brief Remove all given files from the snapshot.
void CppModelManager::removeFilesFromSnapshot(const QSet<QString> &filesToRemove)
{
687
    QMutexLocker snapshotLocker(&d->m_snapshotMutex);
688 689
    QSetIterator<QString> i(filesToRemove);
    while (i.hasNext())
690
        d->m_snapshot.remove(i.next());
691 692
}

693
static QSet<QString> projectPartIds(const QSet<ProjectPart::Ptr> &projectParts)
694
{
695 696 697
    return Utils::transform(projectParts, [](const ProjectPart::Ptr &projectPart) {
        return projectPart->id();
    });
698 699
}

700
class ProjectInfoComparer
701
{
702
public:
703 704
    ProjectInfoComparer(const ProjectInfo &oldProjectInfo,
                        const ProjectInfo &newProjectInfo)
705
        : m_old(oldProjectInfo)
706
        , m_oldSourceFiles(oldProjectInfo.sourceFiles())
707
        , m_new(newProjectInfo)
708
        , m_newSourceFiles(newProjectInfo.sourceFiles())
709 710
    {}

711 712 713
    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); }
714 715 716 717 718 719 720 721 722 723 724 725 726 727 728

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

729 730
    QStringList removedProjectParts()
    {
731 732 733
        QSet<QString> removed = projectPartIds(m_old.projectParts().toSet());
        removed.subtract(projectPartIds(m_new.projectParts().toSet()));
        return removed.toList();
734 735
    }

736 737 738 739 740 741 742 743 744 745 746 747 748 749
    /// 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;
        }

750
        return CppModelManager::timeStampModifiedFiles(documentsToCheck);
751 752 753
    }

private:
754
    const ProjectInfo &m_old;
755 756
    const QSet<QString> m_oldSourceFiles;

757
    const ProjectInfo &m_new;
758 759 760
    const QSet<QString> m_newSourceFiles;
};

761
/// Make sure that m_projectMutex is locked when calling this.
762
void CppModelManager::recalculateProjectPartMappings()
763
{
764
    d->m_projectPartIdToProjectProjectPart.clear();
765 766
    d->m_fileToProjectParts.clear();
    foreach (const ProjectInfo &projectInfo, d->m_projectToProjectsInfo) {
767
        foreach (const ProjectPart::Ptr &projectPart, projectInfo.projectParts()) {
768
            d->m_projectPartIdToProjectProjectPart[projectPart->id()] = projectPart;
769
            foreach (const ProjectFile &cxxFile, projectPart->files)
770 771
                d->m_fileToProjectParts[Utils::FileName::fromString(cxxFile.path)].append(
                            projectPart);
772

773 774
        }
    }
775 776

    d->m_symbolFinder.clearCache();
777 778
}

779
void CppModelManager::updateCppEditorDocuments() const
780
{
781 782
    // Refresh visible documents
    QSet<Core::IDocument *> visibleCppEditorDocuments;
783
    foreach (Core::IEditor *editor, Core::EditorManager::visibleEditors()) {
784
        if (Core::IDocument *document = editor->document()) {
785 786
            const QString filePath = document->filePath().toString();
            if (CppEditorDocumentHandle *theCppEditorDocument = cppEditorDocument(filePath)) {
787
                visibleCppEditorDocuments.insert(document);
788
                theCppEditorDocument->processor()->run();
789
            }
790 791 792
        }
    }

793 794 795 796 797
    // Mark invisible documents dirty
    QSet<Core::IDocument *> invisibleCppEditorDocuments
        = Core::DocumentModel::openedDocuments().toSet();
    invisibleCppEditorDocuments.subtract(visibleCppEditorDocuments);
    foreach (Core::IDocument *document, invisibleCppEditorDocuments) {
798 799 800
        const QString filePath = document->filePath().toString();
        if (CppEditorDocumentHandle *theCppEditorDocument = cppEditorDocument(filePath))
            theCppEditorDocument->setNeedsRefresh(true);
801 802 803
    }
}

804 805 806
QFuture<void> CppModelManager::updateProjectInfo(const ProjectInfo &newProjectInfo)
{
    if (!newProjectInfo.isValid())
807
        return QFuture<void>();
808

809
    QSet<QString> filesToReindex;
810
    QStringList removedProjectParts;
811 812
    bool filesRemoved = false;

813
    { // Only hold the mutex for a limited scope, so the dumping afterwards does not deadlock.
814
        QMutexLocker projectLocker(&d->m_projectMutex);
815 816

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

819
        // Check if we can avoid a full reindexing
820
        ProjectInfo oldProjectInfo = d->m_projectToProjectsInfo.value(project);
821
        if (oldProjectInfo.isValid()) {
822
            ProjectInfoComparer comparer(oldProjectInfo, newProjectInfo);
823

824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844
            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);
845
                }
846

847 848 849 850 851 852 853
                // 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);
                }
854 855
            }

856
            removedProjectParts = comparer.removedProjectParts();
857

858 859
        // A new project was opened/created, do a full indexing
        } else {
860
            d->m_dirty = true;