cppmodelmanager.cpp 44.5 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 "cpprefactoringengine.h"
37
#include "cppsourceprocessor.h"
38
#include "cpptoolsconstants.h"
39
#include "cpptoolsplugin.h"
40
#include "cpptoolsreuse.h"
41
#include "editordocumenthandle.h"
42
#include "symbolfinder.h"
43
#include "followsymbolinterface.h"
con's avatar
con committed
44

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

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

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

73 74
static const bool DumpProjectInfo = qgetenv("QTC_DUMP_PROJECT_INFO") == "1";

75 76
using namespace CppTools;
using namespace CppTools::Internal;
con's avatar
con committed
77 78
using namespace CPlusPlus;

79
#ifdef QTCREATOR_WITH_DUMP_AST
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 117 118

#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
119
#endif // QTCREATOR_WITH_DUMP_AST
120

121
namespace CppTools {
122 123 124

using REType = RefactoringEngineType;

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

static QMutex m_instanceMutex;
static CppModelManager *m_instance;

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

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

    // Editor integration
150 151
    mutable QMutex m_cppEditorDocumentsMutex;
    QMap<QString, CppEditorDocumentHandle *> m_cppEditorDocuments;
152 153
    QSet<AbstractEditorSupport *> m_extraEditorSupports;

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

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

    CppFindReferences *m_findReferences;

165
    SymbolFinder m_symbolFinder;
166
    QThreadPool m_threadPool;
167

168 169
    bool m_enableGC;
    QTimer m_delayedGcTimer;
Marco Bubke's avatar
Marco Bubke committed
170 171

    // Refactoring
172
    using REHash = QMap<REType, RefactoringEngineInterface *>;
173
    REHash m_refactoringEngines;
174 175 176 177 178
};

} // namespace Internal

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

189 190 191 192
    "#define __complex__\n"
    "#define __imag__\n"
    "#define __real__\n"

193 194
    "#define __builtin_va_arg(a,b) ((b)0)\n"

195 196
    "#define _Pragma(x)\n" // C99 _Pragma operator

197 198 199 200 201 202
    "#define __func__ \"\"\n"

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

con's avatar
con committed
203 204
    // ### add macros for win32
    "#define __cdecl\n"
205
    "#define __stdcall\n"
206
    "#define __thiscall\n"
con's avatar
con committed
207 208 209 210 211
    "#define QT_WA(x) x\n"
    "#define CALLBACK\n"
    "#define STDMETHODCALLTYPE\n"
    "#define __RPC_FAR\n"
    "#define __declspec(a)\n"
212 213 214
    "#define STDMETHOD(method) virtual HRESULT STDMETHODCALLTYPE method\n"
    "#define __try try\n"
    "#define __except catch\n"
215 216
    "#define __finally\n"
    "#define __inline inline\n"
217
    "#define __forceinline inline\n"
218 219 220 221 222 223 224 225
    "#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
226

227
QSet<QString> CppModelManager::timeStampModifiedFiles(const QList<Document::Ptr> &documentsToCheck)
228
{
229
    QSet<QString> sourceFiles;
230

231
    foreach (const Document::Ptr doc, documentsToCheck) {
232 233
        const QDateTime lastModified = doc->lastModified();

234
        if (!lastModified.isNull()) {
235 236 237
            QFileInfo fileInfo(doc->fileName());

            if (fileInfo.exists() && fileInfo.lastModified() != lastModified)
238
                sourceFiles.insert(doc->fileName());
239 240 241
        }
    }

242 243 244
    return sourceFiles;
}

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

268 269 270 271 272
QString CppModelManager::editorConfigurationFileName()
{
    return QLatin1String("<per-editor-defines>");
}

273 274 275
static RefactoringEngineInterface *getRefactoringEngine(
        CppModelManagerPrivate::REHash &engines, bool excludeClangCodeModel = true)
{
276
    QTC_ASSERT(!engines.empty(), return nullptr;);
277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
    RefactoringEngineInterface *currentEngine = engines[REType::BuiltIn];
    if (!excludeClangCodeModel && engines.find(REType::ClangCodeModel) != engines.end()) {
        currentEngine = engines[REType::ClangCodeModel];
    } else if (engines.find(REType::ClangRefactoring) != engines.end()) {
        RefactoringEngineInterface *engine = engines[REType::ClangRefactoring];
        if (engine->isRefactoringEngineAvailable())
            currentEngine = engine;
    }
    return currentEngine;
}

void CppModelManager::startLocalRenaming(const CursorInEditor &data,
                                         CppTools::ProjectPart *projectPart,
                                         RenameCallback &&renameSymbolsCallback)
{
    RefactoringEngineInterface *engine = getRefactoringEngine(instance()->d->m_refactoringEngines,
                                                              false);
294
    QTC_ASSERT(engine, return;);
295 296 297
    engine->startLocalRenaming(data, projectPart, std::move(renameSymbolsCallback));
}

298 299
void CppModelManager::globalRename(const CursorInEditor &data, UsagesCallback &&renameCallback,
                                   const QString &replacement)
300 301
{
    RefactoringEngineInterface *engine = getRefactoringEngine(instance()->d->m_refactoringEngines);
302
    QTC_ASSERT(engine, return;);
303
    engine->globalRename(data, std::move(renameCallback), replacement);
304
}
305

306 307 308 309
void CppModelManager::findUsages(const CppTools::CursorInEditor &data,
                                 UsagesCallback &&showUsagesCallback) const
{
    RefactoringEngineInterface *engine = getRefactoringEngine(instance()->d->m_refactoringEngines);
310
    QTC_ASSERT(engine, return;);
311 312 313
    engine->findUsages(data, std::move(showUsagesCallback));
}

314 315
void CppModelManager::addRefactoringEngine(RefactoringEngineType type,
                                           RefactoringEngineInterface *refactoringEngine)
Marco Bubke's avatar
Marco Bubke committed
316
{
317
    instance()->d->m_refactoringEngines[type] = refactoringEngine;
Marco Bubke's avatar
Marco Bubke committed
318 319
}

320
void CppModelManager::removeRefactoringEngine(RefactoringEngineType type)
Marco Bubke's avatar
Marco Bubke committed
321
{
322
    instance()->d->m_refactoringEngines.remove(type);
Marco Bubke's avatar
Marco Bubke committed
323 324
}

325
FollowSymbolInterface &CppModelManager::followSymbolInterface() const
326 327 328 329
{
    return d->m_activeModelManagerSupport->followSymbolInterface();
}

330 331
QString CppModelManager::configurationFileName()
{
332
    return Preprocessor::configurationFileName();
333 334
}

335 336 337 338 339 340 341
void CppModelManager::updateModifiedSourceFiles()
{
    const Snapshot snapshot = this->snapshot();
    QList<Document::Ptr> documentsToCheck;
    foreach (const Document::Ptr document, snapshot)
        documentsToCheck << document;

342
    updateSourceFiles(timeStampModifiedFiles(documentsToCheck));
343 344
}

con's avatar
con committed
345 346
/*!
    \class CppTools::CppModelManager
347 348 349
    \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
350

351 352 353 354
    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
355 356
*/

357 358
CppModelManager *CppModelManager::instance()
{
359 360 361 362 363 364 365 366
    if (m_instance)
        return m_instance;

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

    return m_instance;
367 368
}

369
void CppModelManager::initializeBuiltinModelManagerSupport()
370 371 372 373
{
    d->m_builtinModelManagerSupport
            = ModelManagerSupportProviderInternal().createModelManagerSupport();
    d->m_activeModelManagerSupport = d->m_builtinModelManagerSupport;
374 375
    d->m_refactoringEngines[RefactoringEngineType::BuiltIn] =
            &d->m_activeModelManagerSupport->refactoringEngineInterface();
376 377
}

378
CppModelManager::CppModelManager(QObject *parent)
379
    : CppModelManagerBase(parent), d(new CppModelManagerPrivate)
con's avatar
con committed
380
{
381 382 383
    d->m_indexingSupporter = 0;
    d->m_enableGC = true;

384
    qRegisterMetaType<QSet<QString> >();
385 386
    connect(this, &CppModelManager::sourceFilesRefreshed,
            this, &CppModelManager::onSourceFilesRefreshed);
Erik Verbruggen's avatar
Erik Verbruggen committed
387

388 389
    d->m_findReferences = new CppFindReferences(this);
    d->m_indexerEnabled = qgetenv("QTC_NO_CODE_INDEXER") != "1";
Roberto Raggi's avatar
Roberto Raggi committed
390

391
    d->m_dirty = true;
392

393 394
    d->m_delayedGcTimer.setObjectName(QLatin1String("CppModelManager::m_delayedGcTimer"));
    d->m_delayedGcTimer.setSingleShot(true);
395
    connect(&d->m_delayedGcTimer, &QTimer::timeout, this, &CppModelManager::GC);
396

397 398 399 400 401 402 403
    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);
404 405
    connect(sessionManager, &ProjectExplorer::SessionManager::startupProjectChanged,
            this, &CppModelManager::onActiveProjectChanged);
406 407 408 409

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

410 411
    connect(Core::DocumentManager::instance(), &Core::DocumentManager::allDocumentsRenamed,
            this, &CppModelManager::renameIncludes);
con's avatar
con committed
412

413 414
    connect(Core::ICore::instance(), &Core::ICore::coreAboutToClose,
            this, &CppModelManager::onCoreAboutToClose);
415

con's avatar
con committed
416
    qRegisterMetaType<CPlusPlus::Document::Ptr>("CPlusPlus::Document::Ptr");
417
    qRegisterMetaType<QList<Document::DiagnosticMessage>>(
418
                "QList<CPlusPlus::Document::DiagnosticMessage>");
con's avatar
con committed
419

420
    initializeBuiltinModelManagerSupport();
421

422
    d->m_internalIndexingSupport = new BuiltinIndexingSupport;
con's avatar
con committed
423 424 425
}

CppModelManager::~CppModelManager()
426
{
427
    delete d->m_internalIndexingSupport;
428
    delete d;
429
}
con's avatar
con committed
430

431
Snapshot CppModelManager::snapshot() const
432
{
433 434
    QMutexLocker locker(&d->m_snapshotMutex);
    return d->m_snapshot;
435
}
con's avatar
con committed
436

437 438
Document::Ptr CppModelManager::document(const QString &fileName) const
{
439 440
    QMutexLocker locker(&d->m_snapshotMutex);
    return d->m_snapshot.document(fileName);
441 442 443 444 445 446 447
}

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

450
    Document::Ptr previous = d->m_snapshot.document(newDoc->fileName());
451 452 453 454
    if (previous && (newDoc->revision() != 0 && newDoc->revision() < previous->revision()))
        // the new document is outdated
        return false;

455
    d->m_snapshot.insert(newDoc);
456 457 458
    return true;
}

459
/// Make sure that m_projectMutex is locked when calling this.
460 461
void CppModelManager::ensureUpdated()
{
462
    if (!d->m_dirty)
463 464
        return;

465 466 467 468
    d->m_projectFiles = internalProjectFiles();
    d->m_headerPaths = internalHeaderPaths();
    d->m_definedMacros = internalDefinedMacros();
    d->m_dirty = false;
469 470
}

471
QStringList CppModelManager::internalProjectFiles() const
con's avatar
con committed
472 473
{
    QStringList files;
474
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(d->m_projectToProjectsInfo);
con's avatar
con committed
475 476
    while (it.hasNext()) {
        it.next();
477
        const ProjectInfo pinfo = it.value();
478
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
479 480
            foreach (const ProjectFile &file, part->files)
                files += file.path;
481
        }
con's avatar
con committed
482
    }
483
    files.removeDuplicates();
con's avatar
con committed
484 485 486
    return files;
}

487
ProjectPartHeaderPaths CppModelManager::internalHeaderPaths() const
con's avatar
con committed
488
{
489
    ProjectPartHeaderPaths headerPaths;
490
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(d->m_projectToProjectsInfo);
con's avatar
con committed
491 492
    while (it.hasNext()) {
        it.next();
493
        const ProjectInfo pinfo = it.value();
494
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
495 496
            foreach (const ProjectPartHeaderPath &path, part->headerPaths) {
                const ProjectPartHeaderPath hp(QDir::cleanPath(path.path), path.type);
497 498 499 500
                if (!headerPaths.contains(hp))
                    headerPaths += hp;
            }
        }
con's avatar
con committed
501
    }
502
    return headerPaths;
con's avatar
con committed
503 504
}

505 506 507
static void addUnique(const ProjectExplorer::Macros &newMacros,
                      ProjectExplorer::Macros &macros,
                      QSet<ProjectExplorer::Macro> &alreadyIn)
508
{
509 510 511 512
    for (const ProjectExplorer::Macro &macro : newMacros) {
        if (!alreadyIn.contains(macro)) {
            macros += macro;
            alreadyIn.insert(macro);
513 514 515 516
        }
    }
}

517
ProjectExplorer::Macros CppModelManager::internalDefinedMacros() const
con's avatar
con committed
518
{
519 520
    ProjectExplorer::Macros macros;
    QSet<ProjectExplorer::Macro> alreadyIn;
521
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(d->m_projectToProjectsInfo);
con's avatar
con committed
522 523
    while (it.hasNext()) {
        it.next();
524
        const ProjectInfo pinfo = it.value();
525 526 527
        for (const ProjectPart::Ptr &part : pinfo.projectParts()) {
            addUnique(part->toolChainMacros, macros, alreadyIn);
            addUnique(part->projectMacros, macros, alreadyIn);
528
            if (!part->projectConfigFile.isEmpty())
529
                macros += ProjectExplorer::Macro::toMacros(ProjectPart::readProjectConfigFile(part));
530
        }
con's avatar
con committed
531 532 533 534
    }
    return macros;
}

535
/// This function will acquire mutexes!
536
void CppModelManager::dumpModelManagerConfiguration(const QString &logFileId)
537
{
538 539 540 541
    const Snapshot globalSnapshot = snapshot();
    const QString globalSnapshotTitle
        = QString::fromLatin1("Global/Indexing Snapshot (%1 Documents)").arg(globalSnapshot.size());

542
    CppCodeModelInspector::Dumper dumper(globalSnapshot, logFileId);
543 544 545
    dumper.dumpProjectInfos(projectInfos());
    dumper.dumpSnapshot(globalSnapshot, globalSnapshotTitle, /*isGlobalSnapshot=*/ true);
    dumper.dumpWorkingCopy(workingCopy());
546 547
    dumper.dumpMergedEntities(headerPaths(),
                              ProjectExplorer:: Macro::toByteArray(definedMacros()));
548 549
}

550 551 552 553 554
QSet<AbstractEditorSupport *> CppModelManager::abstractEditorSupports() const
{
    return d->m_extraEditorSupports;
}

555
void CppModelManager::addExtraEditorSupport(AbstractEditorSupport *editorSupport)
556
{
557
    d->m_extraEditorSupports.insert(editorSupport);
558 559
}

560
void CppModelManager::removeExtraEditorSupport(AbstractEditorSupport *editorSupport)
561
{
562
    d->m_extraEditorSupports.remove(editorSupport);
563 564
}

565
CppEditorDocumentHandle *CppModelManager::cppEditorDocument(const QString &filePath) const
566
{
567 568
    if (filePath.isEmpty())
        return 0;
569

570 571
    QMutexLocker locker(&d->m_cppEditorDocumentsMutex);
    return d->m_cppEditorDocuments.value(filePath, 0);
572 573
}

574
void CppModelManager::registerCppEditorDocument(CppEditorDocumentHandle *editorDocument)
575
{
576 577 578
    QTC_ASSERT(editorDocument, return);
    const QString filePath = editorDocument->filePath();
    QTC_ASSERT(!filePath.isEmpty(), return);
579

580 581 582
    QMutexLocker locker(&d->m_cppEditorDocumentsMutex);
    QTC_ASSERT(d->m_cppEditorDocuments.value(filePath, 0) == 0, return);
    d->m_cppEditorDocuments.insert(filePath, editorDocument);
583
}
584

585
void CppModelManager::unregisterCppEditorDocument(const QString &filePath)
586 587
{
    QTC_ASSERT(!filePath.isEmpty(), return);
588

589 590
    static short closedCppDocuments = 0;
    int openCppDocuments = 0;
591

592
    {
593 594 595 596
        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();
597 598
    }

599 600 601
    ++closedCppDocuments;
    if (openCppDocuments == 0 || closedCppDocuments == 5) {
        closedCppDocuments = 0;
602
        delayedGC();
603 604 605
    }
}

606
QList<int> CppModelManager::references(Symbol *symbol, const LookupContext &context)
607
{
608
    return d->m_findReferences->references(symbol, context);
609 610
}

611
void CppModelManager::findUsages(Symbol *symbol, const LookupContext &context)
Roberto Raggi's avatar
Roberto Raggi committed
612
{
613
    if (symbol->identifier())
614
        d->m_findReferences->findUsages(symbol, context);
615 616
}

617 618
void CppModelManager::renameUsages(Symbol *symbol,
                                   const LookupContext &context,
619
                                   const QString &replacement)
620 621
{
    if (symbol->identifier())
622
        d->m_findReferences->renameUsages(symbol, context, replacement);
Roberto Raggi's avatar
Roberto Raggi committed
623 624
}

625
void CppModelManager::findMacroUsages(const CPlusPlus::Macro &macro)
Christian Kamm's avatar
Christian Kamm committed
626
{
627
    d->m_findReferences->findMacroUses(macro);
Christian Kamm's avatar
Christian Kamm committed
628 629
}

630
void CppModelManager::renameMacroUsages(const CPlusPlus::Macro &macro, const QString &replacement)
631
{
632
    d->m_findReferences->renameMacroUses(macro, replacement);
633 634
}

635
void CppModelManager::replaceSnapshot(const Snapshot &newSnapshot)
636
{
637 638
    QMutexLocker snapshotLocker(&d->m_snapshotMutex);
    d->m_snapshot = newSnapshot;
639 640
}

641
WorkingCopy CppModelManager::buildWorkingCopyList()
con's avatar
con committed
642
{
643
    WorkingCopy workingCopy;
644

645 646 647 648 649
    foreach (const CppEditorDocumentHandle *cppEditorDocument, cppEditorDocuments()) {
        workingCopy.insert(cppEditorDocument->filePath(),
                           cppEditorDocument->contents(),
                           cppEditorDocument->revision());
    }
con's avatar
con committed
650

651
    QSetIterator<AbstractEditorSupport *> it(d->m_extraEditorSupports);
652 653
    while (it.hasNext()) {
        AbstractEditorSupport *es = it.next();
654
        workingCopy.insert(es->fileName(), es->contents(), es->revision());
655 656
    }

657 658
    // Add the project configuration file
    QByteArray conf = codeModelConfiguration();
659
    conf += ProjectExplorer::Macro::toByteArray(definedMacros());
660 661
    workingCopy.insert(configurationFileName(), conf);

con's avatar
con committed
662 663 664
    return workingCopy;
}

665
WorkingCopy CppModelManager::workingCopy() const
666 667 668 669
{
    return const_cast<CppModelManager *>(this)->buildWorkingCopyList();
}

670 671 672 673 674
QByteArray CppModelManager::codeModelConfiguration() const
{
    return QByteArray::fromRawData(pp_configuration, qstrlen(pp_configuration));
}

675
static QSet<QString> tooBigFilesRemoved(const QSet<QString> &files, int fileSizeLimitInMb)
676
{
677
    if (fileSizeLimitInMb <= 0)
678 679 680 681 682 683 684 685 686
        return files;

    QSet<QString> result;
    QFileInfo fileInfo;

    QSetIterator<QString> i(files);
    while (i.hasNext()) {
        const QString filePath = i.next();
        fileInfo.setFile(filePath);
687
        if (fileSizeExceedsLimit(fileInfo, fileSizeLimitInMb))
688 689 690 691 692 693 694 695
            continue;

        result << filePath;
    }

    return result;
}

696
QFuture<void> CppModelManager::updateSourceFiles(const QSet<QString> &sourceFiles,
697
                                                 ProgressNotificationMode mode)
698 699 700 701 702 703 704 705
{
    const QFutureInterface<void> dummy;
    return updateSourceFiles(dummy, sourceFiles, mode);
}

QFuture<void> CppModelManager::updateSourceFiles(const QFutureInterface<void> &superFuture,
                                                 const QSet<QString> &sourceFiles,
                                                 ProgressNotificationMode mode)
706
{
707
    if (sourceFiles.isEmpty() || !d->m_indexerEnabled)
708 709
        return QFuture<void>();

710
    const QSet<QString> filteredFiles = tooBigFilesRemoved(sourceFiles, indexerFileSizeLimitInMb());
711

712
    if (d->m_indexingSupporter)
713 714
        d->m_indexingSupporter->refreshSourceFiles(superFuture, filteredFiles, mode);
    return d->m_internalIndexingSupport->refreshSourceFiles(superFuture, filteredFiles, mode);
715
}
con's avatar
con committed
716

717
QList<ProjectInfo> CppModelManager::projectInfos() const
718
{
719 720
    QMutexLocker locker(&d->m_projectMutex);
    return d->m_projectToProjectsInfo.values();
721 722
}

723
ProjectInfo CppModelManager::projectInfo(ProjectExplorer::Project *project) const
724
{
725
    QMutexLocker locker(&d->m_projectMutex);
726
    return d->m_projectToProjectsInfo.value(project, ProjectInfo());
727 728
}

729 730 731 732 733 734
/// \brief Remove all files and their includes (recursively) of given ProjectInfo from the snapshot.
void CppModelManager::removeProjectInfoFilesAndIncludesFromSnapshot(const ProjectInfo &projectInfo)
{
    if (!projectInfo.isValid())
        return;

735
    QMutexLocker snapshotLocker(&d->m_snapshotMutex);
736 737
    foreach (const ProjectPart::Ptr &projectPart, projectInfo.projectParts()) {
        foreach (const ProjectFile &cxxFile, projectPart->files) {
738 739 740
            foreach (const QString &fileName, d->m_snapshot.allIncludesForDocument(cxxFile.path))
                d->m_snapshot.remove(fileName);
            d->m_snapshot.remove(cxxFile.path);
741 742 743 744
        }
    }
}

745
QList<CppEditorDocumentHandle *> CppModelManager::cppEditorDocuments() const
746
{
747 748
    QMutexLocker locker(&d->m_cppEditorDocumentsMutex);
    return d->m_cppEditorDocuments.values();
749 750
}

751 752 753
/// \brief Remove all given files from the snapshot.
void CppModelManager::removeFilesFromSnapshot(const QSet<QString> &filesToRemove)
{
754
    QMutexLocker snapshotLocker(&d->m_snapshotMutex);
755 756
    QSetIterator<QString> i(filesToRemove);
    while (i.hasNext())
757
        d->m_snapshot.remove(i.next());
758 759 760
}

class ProjectInfoComparer
761
{
762
public:
763 764
    ProjectInfoComparer(const ProjectInfo &oldProjectInfo,
                        const ProjectInfo &newProjectInfo)
765
        : m_old(oldProjectInfo)
766
        , m_oldSourceFiles(oldProjectInfo.sourceFiles())
767
        , m_new(newProjectInfo)
768
        , m_newSourceFiles(newProjectInfo.sourceFiles())
769 770
    {}

771 772 773
    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); }
774 775 776 777 778 779 780 781 782 783 784 785 786 787 788

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

789 790
    QStringList removedProjectParts()
    {
791 792
        QSet<QString> removed = projectPartIds(m_old.projectParts());
        removed.subtract(projectPartIds(m_new.projectParts()));
793
        return removed.toList();
794 795
    }

796 797 798 799 800 801 802 803 804 805 806 807 808 809
    /// 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;
        }

810
        return CppModelManager::timeStampModifiedFiles(documentsToCheck);
811 812
    }

813 814 815 816 817 818 819 820 821 822 823
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;
    }

824
private:
825
    const Proje