cppmodelmanager.cpp 41.9 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
3 4
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://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
12 13 14
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
15
**
16 17 18 19 20 21 22
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
con's avatar
con committed
23
**
hjk's avatar
hjk committed
24
****************************************************************************/
hjk's avatar
hjk committed
25

con's avatar
con committed
26
#include "cppmodelmanager.h"
27

28
#include "abstracteditorsupport.h"
29
#include "baseeditordocumentprocessor.h"
30
#include "builtinindexingsupport.h"
31
#include "cppcodemodelinspectordumper.h"
32
#include "cppfindreferences.h"
33
#include "cppindexingsupport.h"
34
#include "cppmodelmanagersupportinternal.h"
35
#include "cpprefactoringchanges.h"
36
#include "cppsourceprocessor.h"
37
#include "cpptoolsconstants.h"
38
#include "cpptoolsplugin.h"
39
#include "cpptoolsreuse.h"
40
#include "editordocumenthandle.h"
41
#include "symbolfinder.h"
con's avatar
con committed
42

43
#include <coreplugin/documentmanager.h>
44 45
#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h>
46 47
#include <coreplugin/editormanager/editormanager.h>
#include <texteditor/textdocument.h>
48
#include <projectexplorer/project.h>
49
#include <projectexplorer/projectexplorer.h>
50
#include <projectexplorer/projectmacro.h>
51
#include <projectexplorer/session.h>
52
#include <extensionsystem/pluginmanager.h>
53
#include <utils/fileutils.h>
hjk's avatar
hjk committed
54
#include <utils/qtcassert.h>
con's avatar
con committed
55

56 57
#include <QCoreApplication>
#include <QDebug>
58
#include <QDir>
59
#include <QFutureWatcher>
60 61
#include <QMutexLocker>
#include <QTextBlock>
62
#include <QThreadPool>
63
#include <QTimer>
64

65 66
#if defined(QTCREATOR_WITH_DUMP_AST) && defined(Q_CC_GNU)
#define WITH_AST_DUMP
67 68
#include <iostream>
#include <sstream>
69
#endif
hjk's avatar
hjk committed
70

71 72
static const bool DumpProjectInfo = qgetenv("QTC_DUMP_PROJECT_INFO") == "1";

73 74
using namespace CppTools;
using namespace CppTools::Internal;
con's avatar
con committed
75 76
using namespace CPlusPlus;

77
#ifdef QTCREATOR_WITH_DUMP_AST
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 114 115 116

#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
117
#endif // QTCREATOR_WITH_DUMP_AST
118

119 120 121 122 123 124 125 126 127 128 129
namespace CppTools {
namespace Internal {

static QMutex m_instanceMutex;
static CppModelManager *m_instance;

class CppModelManagerPrivate
{
public:
    // Snapshot
    mutable QMutex m_snapshotMutex;
130
    Snapshot m_snapshot;
131 132 133 134

    // Project integration
    mutable QMutex m_projectMutex;
    QMap<ProjectExplorer::Project *, ProjectInfo> m_projectToProjectsInfo;
135
    QHash<ProjectExplorer::Project *, bool> m_projectToIndexerCanceled;
136
    QMap<Utils::FileName, QList<ProjectPart::Ptr> > m_fileToProjectParts;
137
    QMap<QString, ProjectPart::Ptr> m_projectPartIdToProjectProjectPart;
138 139 140
    // The members below are cached/(re)calculated from the projects and/or their project parts
    bool m_dirty;
    QStringList m_projectFiles;
141
    ProjectPartHeaderPaths m_headerPaths;
142
    ProjectExplorer::Macros m_definedMacros;
143 144

    // Editor integration
145 146
    mutable QMutex m_cppEditorDocumentsMutex;
    QMap<QString, CppEditorDocumentHandle *> m_cppEditorDocuments;
147 148
    QSet<AbstractEditorSupport *> m_extraEditorSupports;

149 150 151
    // Model Manager Supports for e.g. completion and highlighting
    ModelManagerSupport::Ptr m_builtinModelManagerSupport;
    ModelManagerSupport::Ptr m_activeModelManagerSupport;
152 153 154 155 156 157 158 159

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

    CppFindReferences *m_findReferences;

160
    SymbolFinder m_symbolFinder;
161
    QThreadPool m_threadPool;
162

163 164
    bool m_enableGC;
    QTimer m_delayedGcTimer;
Marco Bubke's avatar
Marco Bubke committed
165 166 167

    // Refactoring
    RefactoringEngineInterface *m_refactoringEngine = nullptr;
168 169 170 171 172
};

} // namespace Internal

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

183 184 185 186
    "#define __complex__\n"
    "#define __imag__\n"
    "#define __real__\n"

187 188
    "#define __builtin_va_arg(a,b) ((b)0)\n"

189 190
    "#define _Pragma(x)\n" // C99 _Pragma operator

191 192 193 194 195 196
    "#define __func__ \"\"\n"

    // ### add macros for gcc
    "#define __PRETTY_FUNCTION__ \"\"\n"
    "#define __FUNCTION__ \"\"\n"

con's avatar
con committed
197 198
    // ### add macros for win32
    "#define __cdecl\n"
199
    "#define __stdcall\n"
200
    "#define __thiscall\n"
con's avatar
con committed
201 202 203 204 205
    "#define QT_WA(x) x\n"
    "#define CALLBACK\n"
    "#define STDMETHODCALLTYPE\n"
    "#define __RPC_FAR\n"
    "#define __declspec(a)\n"
206 207 208
    "#define STDMETHOD(method) virtual HRESULT STDMETHODCALLTYPE method\n"
    "#define __try try\n"
    "#define __except catch\n"
209 210
    "#define __finally\n"
    "#define __inline inline\n"
211
    "#define __forceinline inline\n"
212 213 214 215 216 217 218 219
    "#define __pragma(x)\n"
    "#define __w64\n"
    "#define __int64 long long\n"
    "#define __int32 long\n"
    "#define __int16 short\n"
    "#define __int8 char\n"
    "#define __ptr32\n"
    "#define __ptr64\n";
con's avatar
con committed
220

221
QSet<QString> CppModelManager::timeStampModifiedFiles(const QList<Document::Ptr> &documentsToCheck)
222
{
223
    QSet<QString> sourceFiles;
224

225
    foreach (const Document::Ptr doc, documentsToCheck) {
226 227
        const QDateTime lastModified = doc->lastModified();

228
        if (!lastModified.isNull()) {
229 230 231
            QFileInfo fileInfo(doc->fileName());

            if (fileInfo.exists() && fileInfo.lastModified() != lastModified)
232
                sourceFiles.insert(doc->fileName());
233 234 235
        }
    }

236 237 238
    return sourceFiles;
}

239 240 241 242 243 244 245 246 247 248 249 250
/*!
 * \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
251
    return new CppSourceProcessor(that->snapshot(), [that](const Document::Ptr &doc) {
252 253 254 255 256
        const Document::Ptr previousDocument = that->document(doc->fileName());
        const unsigned newRevision = previousDocument.isNull()
                ? 1U
                : previousDocument->revision() + 1;
        doc->setRevision(newRevision);
257 258 259 260 261
        that->emitDocumentUpdated(doc);
        doc->releaseSourceAndAST();
    });
}

262 263 264 265 266
QString CppModelManager::editorConfigurationFileName()
{
    return QLatin1String("<per-editor-defines>");
}

Marco Bubke's avatar
Marco Bubke committed
267 268 269 270 271 272 273 274 275 276
void CppModelManager::setRefactoringEngine(RefactoringEngineInterface *refactoringEngine)
{
    instance()->d->m_refactoringEngine = refactoringEngine;
}

RefactoringEngineInterface *CppModelManager::refactoringEngine()
{
    return instance()->d->m_refactoringEngine;
}

277 278
QString CppModelManager::configurationFileName()
{
279
    return Preprocessor::configurationFileName();
280 281
}

282 283 284 285 286 287 288
void CppModelManager::updateModifiedSourceFiles()
{
    const Snapshot snapshot = this->snapshot();
    QList<Document::Ptr> documentsToCheck;
    foreach (const Document::Ptr document, snapshot)
        documentsToCheck << document;

289
    updateSourceFiles(timeStampModifiedFiles(documentsToCheck));
290 291
}

con's avatar
con committed
292 293
/*!
    \class CppTools::CppModelManager
294 295 296
    \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
297

298 299 300 301
    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
302 303
*/

304 305
CppModelManager *CppModelManager::instance()
{
306 307 308 309 310 311 312 313
    if (m_instance)
        return m_instance;

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

    return m_instance;
314 315
}

316
void CppModelManager::initializeBuiltinModelManagerSupport()
317 318 319 320 321 322
{
    d->m_builtinModelManagerSupport
            = ModelManagerSupportProviderInternal().createModelManagerSupport();
    d->m_activeModelManagerSupport = d->m_builtinModelManagerSupport;
}

323
CppModelManager::CppModelManager(QObject *parent)
324
    : CppModelManagerBase(parent), d(new CppModelManagerPrivate)
con's avatar
con committed
325
{
326 327 328
    d->m_indexingSupporter = 0;
    d->m_enableGC = true;

329
    qRegisterMetaType<QSet<QString> >();
330 331
    connect(this, &CppModelManager::sourceFilesRefreshed,
            this, &CppModelManager::onSourceFilesRefreshed);
Erik Verbruggen's avatar
Erik Verbruggen committed
332

333 334
    d->m_findReferences = new CppFindReferences(this);
    d->m_indexerEnabled = qgetenv("QTC_NO_CODE_INDEXER") != "1";
Roberto Raggi's avatar
Roberto Raggi committed
335

336
    d->m_dirty = true;
337

338 339
    d->m_delayedGcTimer.setObjectName(QLatin1String("CppModelManager::m_delayedGcTimer"));
    d->m_delayedGcTimer.setSingleShot(true);
340
    connect(&d->m_delayedGcTimer, &QTimer::timeout, this, &CppModelManager::GC);
341

342 343 344 345 346 347 348
    auto sessionManager = ProjectExplorer::SessionManager::instance();
    connect(sessionManager, &ProjectExplorer::SessionManager::projectAdded,
            this, &CppModelManager::onProjectAdded);
    connect(sessionManager, &ProjectExplorer::SessionManager::aboutToRemoveProject,
            this, &CppModelManager::onAboutToRemoveProject);
    connect(sessionManager, &ProjectExplorer::SessionManager::aboutToLoadSession,
            this, &CppModelManager::onAboutToLoadSession);
349 350
    connect(sessionManager, &ProjectExplorer::SessionManager::startupProjectChanged,
            this, &CppModelManager::onActiveProjectChanged);
351 352 353 354

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

355 356
    connect(Core::DocumentManager::instance(), &Core::DocumentManager::allDocumentsRenamed,
            this, &CppModelManager::renameIncludes);
con's avatar
con committed
357

358 359
    connect(Core::ICore::instance(), &Core::ICore::coreAboutToClose,
            this, &CppModelManager::onCoreAboutToClose);
360

con's avatar
con committed
361
    qRegisterMetaType<CPlusPlus::Document::Ptr>("CPlusPlus::Document::Ptr");
362
    qRegisterMetaType<QList<Document::DiagnosticMessage>>(
363
                "QList<CPlusPlus::Document::DiagnosticMessage>");
con's avatar
con committed
364

365
    initializeBuiltinModelManagerSupport();
366

367
    d->m_internalIndexingSupport = new BuiltinIndexingSupport;
con's avatar
con committed
368 369 370
}

CppModelManager::~CppModelManager()
371
{
372
    delete d->m_internalIndexingSupport;
373
    delete d;
374
}
con's avatar
con committed
375

376
Snapshot CppModelManager::snapshot() const
377
{
378 379
    QMutexLocker locker(&d->m_snapshotMutex);
    return d->m_snapshot;
380
}
con's avatar
con committed
381

382 383
Document::Ptr CppModelManager::document(const QString &fileName) const
{
384 385
    QMutexLocker locker(&d->m_snapshotMutex);
    return d->m_snapshot.document(fileName);
386 387 388 389 390 391 392
}

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

395
    Document::Ptr previous = d->m_snapshot.document(newDoc->fileName());
396 397 398 399
    if (previous && (newDoc->revision() != 0 && newDoc->revision() < previous->revision()))
        // the new document is outdated
        return false;

400
    d->m_snapshot.insert(newDoc);
401 402 403
    return true;
}

404
/// Make sure that m_projectMutex is locked when calling this.
405 406
void CppModelManager::ensureUpdated()
{
407
    if (!d->m_dirty)
408 409
        return;

410 411 412 413
    d->m_projectFiles = internalProjectFiles();
    d->m_headerPaths = internalHeaderPaths();
    d->m_definedMacros = internalDefinedMacros();
    d->m_dirty = false;
414 415
}

416
QStringList CppModelManager::internalProjectFiles() const
con's avatar
con committed
417 418
{
    QStringList files;
419
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(d->m_projectToProjectsInfo);
con's avatar
con committed
420 421
    while (it.hasNext()) {
        it.next();
422
        const ProjectInfo pinfo = it.value();
423
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
424 425
            foreach (const ProjectFile &file, part->files)
                files += file.path;
426
        }
con's avatar
con committed
427
    }
428
    files.removeDuplicates();
con's avatar
con committed
429 430 431
    return files;
}

432
ProjectPartHeaderPaths CppModelManager::internalHeaderPaths() const
con's avatar
con committed
433
{
434
    ProjectPartHeaderPaths headerPaths;
435
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(d->m_projectToProjectsInfo);
con's avatar
con committed
436 437
    while (it.hasNext()) {
        it.next();
438
        const ProjectInfo pinfo = it.value();
439
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
440 441
            foreach (const ProjectPartHeaderPath &path, part->headerPaths) {
                const ProjectPartHeaderPath hp(QDir::cleanPath(path.path), path.type);
442 443 444 445
                if (!headerPaths.contains(hp))
                    headerPaths += hp;
            }
        }
con's avatar
con committed
446
    }
447
    return headerPaths;
con's avatar
con committed
448 449
}

450 451 452
static void addUnique(const ProjectExplorer::Macros &newMacros,
                      ProjectExplorer::Macros &macros,
                      QSet<ProjectExplorer::Macro> &alreadyIn)
453
{
454 455 456 457
    for (const ProjectExplorer::Macro &macro : newMacros) {
        if (!alreadyIn.contains(macro)) {
            macros += macro;
            alreadyIn.insert(macro);
458 459 460 461
        }
    }
}

462
ProjectExplorer::Macros CppModelManager::internalDefinedMacros() const
con's avatar
con committed
463
{
464 465
    ProjectExplorer::Macros macros;
    QSet<ProjectExplorer::Macro> alreadyIn;
466
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(d->m_projectToProjectsInfo);
con's avatar
con committed
467 468
    while (it.hasNext()) {
        it.next();
469
        const ProjectInfo pinfo = it.value();
470 471 472
        for (const ProjectPart::Ptr &part : pinfo.projectParts()) {
            addUnique(part->toolChainMacros, macros, alreadyIn);
            addUnique(part->projectMacros, macros, alreadyIn);
473
            if (!part->projectConfigFile.isEmpty())
474
                macros += ProjectExplorer::Macro::toMacros(ProjectPart::readProjectConfigFile(part));
475
        }
con's avatar
con committed
476 477 478 479
    }
    return macros;
}

480
/// This function will acquire mutexes!
481
void CppModelManager::dumpModelManagerConfiguration(const QString &logFileId)
482
{
483 484 485 486
    const Snapshot globalSnapshot = snapshot();
    const QString globalSnapshotTitle
        = QString::fromLatin1("Global/Indexing Snapshot (%1 Documents)").arg(globalSnapshot.size());

487
    CppCodeModelInspector::Dumper dumper(globalSnapshot, logFileId);
488 489 490
    dumper.dumpProjectInfos(projectInfos());
    dumper.dumpSnapshot(globalSnapshot, globalSnapshotTitle, /*isGlobalSnapshot=*/ true);
    dumper.dumpWorkingCopy(workingCopy());
491 492
    dumper.dumpMergedEntities(headerPaths(),
                              ProjectExplorer:: Macro::toByteArray(definedMacros()));
493 494
}

495 496 497 498 499
QSet<AbstractEditorSupport *> CppModelManager::abstractEditorSupports() const
{
    return d->m_extraEditorSupports;
}

500
void CppModelManager::addExtraEditorSupport(AbstractEditorSupport *editorSupport)
501
{
502
    d->m_extraEditorSupports.insert(editorSupport);
503 504
}

505
void CppModelManager::removeExtraEditorSupport(AbstractEditorSupport *editorSupport)
506
{
507
    d->m_extraEditorSupports.remove(editorSupport);
508 509
}

510
CppEditorDocumentHandle *CppModelManager::cppEditorDocument(const QString &filePath) const
511
{
512 513
    if (filePath.isEmpty())
        return 0;
514

515 516
    QMutexLocker locker(&d->m_cppEditorDocumentsMutex);
    return d->m_cppEditorDocuments.value(filePath, 0);
517 518
}

519
void CppModelManager::registerCppEditorDocument(CppEditorDocumentHandle *editorDocument)
520
{
521 522 523
    QTC_ASSERT(editorDocument, return);
    const QString filePath = editorDocument->filePath();
    QTC_ASSERT(!filePath.isEmpty(), return);
524

525 526 527
    QMutexLocker locker(&d->m_cppEditorDocumentsMutex);
    QTC_ASSERT(d->m_cppEditorDocuments.value(filePath, 0) == 0, return);
    d->m_cppEditorDocuments.insert(filePath, editorDocument);
528
}
529

530
void CppModelManager::unregisterCppEditorDocument(const QString &filePath)
531 532
{
    QTC_ASSERT(!filePath.isEmpty(), return);
533

534 535
    static short closedCppDocuments = 0;
    int openCppDocuments = 0;
536

537
    {
538 539 540 541
        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();
542 543
    }

544 545 546
    ++closedCppDocuments;
    if (openCppDocuments == 0 || closedCppDocuments == 5) {
        closedCppDocuments = 0;
547
        delayedGC();
548 549 550
    }
}

551
QList<int> CppModelManager::references(Symbol *symbol, const LookupContext &context)
552
{
553
    return d->m_findReferences->references(symbol, context);
554 555
}

556
void CppModelManager::findUsages(Symbol *symbol, const LookupContext &context)
Roberto Raggi's avatar
Roberto Raggi committed
557
{
558
    if (symbol->identifier())
559
        d->m_findReferences->findUsages(symbol, context);
560 561
}

562 563
void CppModelManager::renameUsages(Symbol *symbol,
                                   const LookupContext &context,
564
                                   const QString &replacement)
565 566
{
    if (symbol->identifier())
567
        d->m_findReferences->renameUsages(symbol, context, replacement);
Roberto Raggi's avatar
Roberto Raggi committed
568 569
}

570
void CppModelManager::findMacroUsages(const CPlusPlus::Macro &macro)
Christian Kamm's avatar
Christian Kamm committed
571
{
572
    d->m_findReferences->findMacroUses(macro);
Christian Kamm's avatar
Christian Kamm committed
573 574
}

575
void CppModelManager::renameMacroUsages(const CPlusPlus::Macro &macro, const QString &replacement)
576
{
577
    d->m_findReferences->renameMacroUses(macro, replacement);
578 579
}

580
void CppModelManager::replaceSnapshot(const Snapshot &newSnapshot)
581
{
582 583
    QMutexLocker snapshotLocker(&d->m_snapshotMutex);
    d->m_snapshot = newSnapshot;
584 585
}

586
WorkingCopy CppModelManager::buildWorkingCopyList()
con's avatar
con committed
587
{
588
    WorkingCopy workingCopy;
589

590 591 592 593 594
    foreach (const CppEditorDocumentHandle *cppEditorDocument, cppEditorDocuments()) {
        workingCopy.insert(cppEditorDocument->filePath(),
                           cppEditorDocument->contents(),
                           cppEditorDocument->revision());
    }
con's avatar
con committed
595

596
    QSetIterator<AbstractEditorSupport *> it(d->m_extraEditorSupports);
597 598
    while (it.hasNext()) {
        AbstractEditorSupport *es = it.next();
599
        workingCopy.insert(es->fileName(), es->contents(), es->revision());
600 601
    }

602 603
    // Add the project configuration file
    QByteArray conf = codeModelConfiguration();
604
    conf += ProjectExplorer::Macro::toByteArray(definedMacros());
605 606
    workingCopy.insert(configurationFileName(), conf);

con's avatar
con committed
607 608 609
    return workingCopy;
}

610
WorkingCopy CppModelManager::workingCopy() const
611 612 613 614
{
    return const_cast<CppModelManager *>(this)->buildWorkingCopyList();
}

615 616 617 618 619
QByteArray CppModelManager::codeModelConfiguration() const
{
    return QByteArray::fromRawData(pp_configuration, qstrlen(pp_configuration));
}

620
static QSet<QString> tooBigFilesRemoved(const QSet<QString> &files, int fileSizeLimitInMb)
621
{
622
    if (fileSizeLimitInMb <= 0)
623 624 625 626 627 628 629 630 631
        return files;

    QSet<QString> result;
    QFileInfo fileInfo;

    QSetIterator<QString> i(files);
    while (i.hasNext()) {
        const QString filePath = i.next();
        fileInfo.setFile(filePath);
632
        if (fileSizeExceedsLimit(fileInfo, fileSizeLimitInMb))
633 634 635 636 637 638 639 640
            continue;

        result << filePath;
    }

    return result;
}

641
QFuture<void> CppModelManager::updateSourceFiles(const QSet<QString> &sourceFiles,
642
                                                 ProgressNotificationMode mode)
643 644 645 646 647 648 649 650
{
    const QFutureInterface<void> dummy;
    return updateSourceFiles(dummy, sourceFiles, mode);
}

QFuture<void> CppModelManager::updateSourceFiles(const QFutureInterface<void> &superFuture,
                                                 const QSet<QString> &sourceFiles,
                                                 ProgressNotificationMode mode)
651
{
652
    if (sourceFiles.isEmpty() || !d->m_indexerEnabled)
653 654
        return QFuture<void>();

655
    const QSet<QString> filteredFiles = tooBigFilesRemoved(sourceFiles, indexerFileSizeLimitInMb());
656

657
    if (d->m_indexingSupporter)
658 659
        d->m_indexingSupporter->refreshSourceFiles(superFuture, filteredFiles, mode);
    return d->m_internalIndexingSupport->refreshSourceFiles(superFuture, filteredFiles, mode);
660
}
con's avatar
con committed
661

662
QList<ProjectInfo> CppModelManager::projectInfos() const
663
{
664 665
    QMutexLocker locker(&d->m_projectMutex);
    return d->m_projectToProjectsInfo.values();
666 667
}

668
ProjectInfo CppModelManager::projectInfo(ProjectExplorer::Project *project) const
669
{
670
    QMutexLocker locker(&d->m_projectMutex);
671
    return d->m_projectToProjectsInfo.value(project, ProjectInfo());
672 673
}

674 675 676 677 678 679
/// \brief Remove all files and their includes (recursively) of given ProjectInfo from the snapshot.
void CppModelManager::removeProjectInfoFilesAndIncludesFromSnapshot(const ProjectInfo &projectInfo)
{
    if (!projectInfo.isValid())
        return;

680
    QMutexLocker snapshotLocker(&d->m_snapshotMutex);
681 682
    foreach (const ProjectPart::Ptr &projectPart, projectInfo.projectParts()) {
        foreach (const ProjectFile &cxxFile, projectPart->files) {
683 684 685
            foreach (const QString &fileName, d->m_snapshot.allIncludesForDocument(cxxFile.path))
                d->m_snapshot.remove(fileName);
            d->m_snapshot.remove(cxxFile.path);
686 687 688 689
        }
    }
}

690
QList<CppEditorDocumentHandle *> CppModelManager::cppEditorDocuments() const
691
{
692 693
    QMutexLocker locker(&d->m_cppEditorDocumentsMutex);
    return d->m_cppEditorDocuments.values();
694 695
}

696 697 698
/// \brief Remove all given files from the snapshot.
void CppModelManager::removeFilesFromSnapshot(const QSet<QString> &filesToRemove)
{
699
    QMutexLocker snapshotLocker(&d->m_snapshotMutex);
700 701
    QSetIterator<QString> i(filesToRemove);
    while (i.hasNext())
702
        d->m_snapshot.remove(i.next());
703 704 705
}

class ProjectInfoComparer
706
{
707
public:
708 709
    ProjectInfoComparer(const ProjectInfo &oldProjectInfo,
                        const ProjectInfo &newProjectInfo)
710
        : m_old(oldProjectInfo)
711
        , m_oldSourceFiles(oldProjectInfo.sourceFiles())
712
        , m_new(newProjectInfo)
713
        , m_newSourceFiles(newProjectInfo.sourceFiles())
714 715
    {}

716 717 718
    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); }
719 720 721 722 723 724 725 726 727 728 729 730 731 732 733

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

734 735
    QStringList removedProjectParts()
    {
736 737
        QSet<QString> removed = projectPartIds(m_old.projectParts());
        removed.subtract(projectPartIds(m_new.projectParts()));
738
        return removed.toList();
739 740
    }

741 742 743 744 745 746 747 748 749 750 751 752 753 754
    /// 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;
        }

755
        return CppModelManager::timeStampModifiedFiles(documentsToCheck);
756 757
    }

758 759 760 761 762 763 764 765 766 767 768
private:
    static QSet<QString> projectPartIds(const QVector<ProjectPart::Ptr> &projectParts)
    {
        QSet<QString> ids;

        foreach (const ProjectPart::Ptr &projectPart, projectParts)
            ids.insert(projectPart->id());

        return ids;
    }

769
private:
770
    const ProjectInfo &m_old;
771 772
    const QSet<QString> m_oldSourceFiles;

773
    const ProjectInfo &m_new;
774 775 776
    const QSet<QString> m_newSourceFiles;
};

777
/// Make sure that m_projectMutex is locked when calling this.
778
void CppModelManager::recalculateProjectPartMappings()
779
{
780
    d->m_projectPartIdToProjectProjectPart.clear();
781 782
    d->m_fileToProjectParts.clear();
    foreach (const ProjectInfo &projectInfo, d->m_projectToProjectsInfo) {
783
        foreach (const ProjectPart::Ptr &projectPart, projectInfo.projectParts()) {
784
            d->m_projectPartIdToProjectProjectPart[projectPart->id()] = projectPart;
785
            foreach (const ProjectFile &cxxFile, projectPart->files)
786 787
                d->m_fileToProjectParts[Utils::FileName::fromString(cxxFile.path)].append(
                            projectPart);
788

789 790
        }
    }
791 792

    d->m_symbolFinder.clearCache();
793 794
}

795
void CppModelManager::watchForCanceledProjectIndexer(const QVector<QFuture<void>> &futures,
796 797 798 799
                                                     ProjectExplorer::Project *project)
{
    d->m_projectToIndexerCanceled.insert(project, false);

800 801 802
    for (const QFuture<void> &future : futures) {
        if (future.isCanceled() || future.isFinished())
            continue;
803

804 805 806 807 808 809 810 811 812 813
        QFutureWatcher<void> *watcher = new QFutureWatcher<void>();
        connect(watcher, &QFutureWatcher<void>::canceled, this, [this, project]() {
            if (d->m_projectToIndexerCanceled.contains(project)) // Project not yet removed
                d->m_projectToIndexerCanceled.insert(project, true);
        });
        connect(watcher, &QFutureWatcher<void>::finished, this, [watcher]() {
            watcher->deleteLater();
        });
        watcher->setFuture(future);
    }
814 815
}

816
void CppModelManager::updateCppEditorDocuments(bool projectsUpdated) const
817
{
818 819
    // Refresh visible documents
    QSet<Core::IDocument *> visibleCppEditorDocuments;
820
    foreach (Core::IEditor *editor, Core::EditorManager::visibleEditors()) {
821
        if (Core::IDocument *document = editor->document()) {
822 823
            const QString filePath = document->filePath().toString();
            if (CppEditorDocumentHandle *theCppEditorDocument = cppEditorDocument(filePath)) {
824
                visibleCppEditorDocuments.insert(document);
825
                theCppEditorDocument->processor()->run(projectsUpdated);