cppmodelmanager.cpp 34.9 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
3
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
con's avatar
con committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
con's avatar
con committed
7
**
hjk's avatar
hjk committed
8 9 10 11 12
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
Eike Ziller's avatar
Eike Ziller committed
13 14
** conditions see http://www.qt.io/licensing.  For further information
** use the contact form at http://www.qt.io/contact-us.
15
**
16
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
** Alternatively, this file may be used under the terms of the GNU Lesser
Eike Ziller's avatar
Eike Ziller committed
18 19 20 21 22 23
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
hjk's avatar
hjk committed
24 25 26
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
con's avatar
con committed
27 28
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
29
****************************************************************************/
hjk's avatar
hjk committed
30

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

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

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

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

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

66 67
Q_DECLARE_METATYPE(QSet<QString>)

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

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

74
#ifdef QTCREATOR_WITH_DUMP_AST
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113

#include <cxxabi.h>

class DumpAST: protected ASTVisitor
{
public:
    int depth;

    DumpAST(Control *control)
        : ASTVisitor(control), depth(0)
    { }

    void operator()(AST *ast)
    { accept(ast); }

protected:
    virtual bool preVisit(AST *ast)
    {
        std::ostringstream s;
        PrettyPrinter pp(control(), s);
        pp(ast);
        QString code = QString::fromStdString(s.str());
        code.replace('\n', ' ');
        code.replace(QRegExp("\\s+"), " ");

        const char *name = abi::__cxa_demangle(typeid(*ast).name(), 0, 0, 0) + 11;

        QByteArray ind(depth, ' ');
        ind += name;

        printf("%-40s %s\n", ind.constData(), qPrintable(code));
        ++depth;
        return true;
    }

    virtual void postVisit(AST *)
    { --depth; }
};

Roberto Raggi's avatar
Roberto Raggi committed
114
#endif // QTCREATOR_WITH_DUMP_AST
115

116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
namespace CppTools {
namespace Internal {

static QMutex m_instanceMutex;
static CppModelManager *m_instance;

class CppModelManagerPrivate
{
public:
    // Snapshot
    mutable QMutex m_snapshotMutex;
    CPlusPlus::Snapshot m_snapshot;

    // Project integration
    mutable QMutex m_projectMutex;
    QMap<ProjectExplorer::Project *, ProjectInfo> m_projectToProjectsInfo;
    QMap<QString, QList<CppTools::ProjectPart::Ptr> > m_fileToProjectParts;
    QMap<QString, CppTools::ProjectPart::Ptr> m_projectFileToProjectPart;
    // The members below are cached/(re)calculated from the projects and/or their project parts
    bool m_dirty;
    QStringList m_projectFiles;
    ProjectPart::HeaderPaths m_headerPaths;
    QByteArray m_definedMacros;

    // Editor integration
    mutable QMutex m_cppEditorsMutex;
    QMap<QString, EditorDocumentHandle *> m_cppEditors;
    QSet<AbstractEditorSupport *> m_extraEditorSupports;

    // Completion & highlighting
    QHash<QString, ModelManagerSupport *> m_idTocodeModelSupporter;
    QScopedPointer<ModelManagerSupport> m_modelManagerSupportFallback;

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

    CppFindReferences *m_findReferences;

    bool m_enableGC;
    QTimer m_delayedGcTimer;
};

} // namespace Internal

const char pp_configuration[] =
con's avatar
con committed
163
    "# 1 \"<configuration>\"\n"
164
    "#define Q_CREATOR_RUN 1\n"
con's avatar
con committed
165 166 167 168 169 170
    "#define __cplusplus 1\n"
    "#define __extension__\n"
    "#define __context__\n"
    "#define __range__\n"
    "#define   restrict\n"
    "#define __restrict\n"
171
    "#define __restrict__\n"
con's avatar
con committed
172

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

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

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

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

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

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

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

210 211 212
    return sourceFiles;
}

213 214 215 216 217 218 219 220 221 222 223 224
/*!
 * \brief createSourceProcessor Create a new source processor, which will signal the
 * model manager when a document has been processed.
 *
 * Indexed file is truncated version of fully parsed document: copy of source
 * code and full AST will be dropped when indexing is done.
 *
 * \return a new source processor object, which the caller needs to delete when finished.
 */
CppSourceProcessor *CppModelManager::createSourceProcessor()
{
    CppModelManager *that = instance();
Orgad Shaneh's avatar
Orgad Shaneh committed
225
    return new CppSourceProcessor(that->snapshot(), [that](const Document::Ptr &doc) {
226 227 228 229 230
        that->emitDocumentUpdated(doc);
        doc->releaseSourceAndAST();
    });
}

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

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

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

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

con's avatar
con committed
251 252
/*!
    \class CppTools::CppModelManager
253 254 255
    \brief The CppModelManager keeps tracks of the source files the code model is aware of.

    The CppModelManager manages the source files in a Snapshot object.
con's avatar
con committed
256

257 258 259 260
    The snapshot is updated in case e.g.
        * New files are opened/edited (Editor integration)
        * A project manager pushes updated project information (Project integration)
        * Files are garbage collected
con's avatar
con committed
261 262
*/

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

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

    return m_instance;
273 274
}

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

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

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

288
    d->m_dirty = true;
289

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

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

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

307 308
    connect(Core::DocumentManager::instance(), &Core::DocumentManager::allDocumentsRenamed,
            this, &CppModelManager::renameIncludes);
con's avatar
con committed
309

310 311 312
    connect(Core::ICore::instance(), SIGNAL(coreAboutToClose()),
            this, SLOT(onCoreAboutToClose()));

con's avatar
con committed
313
    qRegisterMetaType<CPlusPlus::Document::Ptr>("CPlusPlus::Document::Ptr");
314 315
    qRegisterMetaType<QList<CPlusPlus::Document::DiagnosticMessage>>(
                "QList<CPlusPlus::Document::DiagnosticMessage>");
con's avatar
con committed
316

317
    d->m_modelManagerSupportFallback.reset(new ModelManagerSupportInternal);
318
    CppToolsPlugin::instance()->codeModelSettings()->setDefaultId(
319 320
                d->m_modelManagerSupportFallback->id());
    addModelManagerSupport(d->m_modelManagerSupportFallback.data());
321

322
    d->m_internalIndexingSupport = new BuiltinIndexingSupport;
con's avatar
con committed
323 324 325
}

CppModelManager::~CppModelManager()
326
{
327
    delete d->m_internalIndexingSupport;
328
    delete d;
329
}
con's avatar
con committed
330

331
Snapshot CppModelManager::snapshot() const
332
{
333 334
    QMutexLocker locker(&d->m_snapshotMutex);
    return d->m_snapshot;
335
}
con's avatar
con committed
336

337 338
Document::Ptr CppModelManager::document(const QString &fileName) const
{
339 340
    QMutexLocker locker(&d->m_snapshotMutex);
    return d->m_snapshot.document(fileName);
341 342 343 344 345 346 347
}

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

350
    Document::Ptr previous = d->m_snapshot.document(newDoc->fileName());
351 352 353 354
    if (previous && (newDoc->revision() != 0 && newDoc->revision() < previous->revision()))
        // the new document is outdated
        return false;

355
    d->m_snapshot.insert(newDoc);
356 357 358
    return true;
}

359 360
void CppModelManager::ensureUpdated()
{
361 362
    QMutexLocker locker(&d->m_projectMutex);
    if (!d->m_dirty)
363 364
        return;

365 366 367 368
    d->m_projectFiles = internalProjectFiles();
    d->m_headerPaths = internalHeaderPaths();
    d->m_definedMacros = internalDefinedMacros();
    d->m_dirty = false;
369 370
}

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

387
ProjectPart::HeaderPaths CppModelManager::internalHeaderPaths() const
con's avatar
con committed
388
{
389
    ProjectPart::HeaderPaths headerPaths;
390
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(d->m_projectToProjectsInfo);
con's avatar
con committed
391 392
    while (it.hasNext()) {
        it.next();
393
        const ProjectInfo pinfo = it.value();
394 395 396 397 398 399 400 401
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
            foreach (const ProjectPart::HeaderPath &path, part->headerPaths) {
                const ProjectPart::HeaderPath hp(CppSourceProcessor::cleanPath(path.path),
                                                 path.type);
                if (!headerPaths.contains(hp))
                    headerPaths += hp;
            }
        }
con's avatar
con committed
402
    }
403
    return headerPaths;
con's avatar
con committed
404 405
}

406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421
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);
        }
    }
}

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

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

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

455
void CppModelManager::addExtraEditorSupport(AbstractEditorSupport *editorSupport)
456
{
457
    d->m_extraEditorSupports.insert(editorSupport);
458 459
}

460
void CppModelManager::removeExtraEditorSupport(AbstractEditorSupport *editorSupport)
461
{
462
    d->m_extraEditorSupports.remove(editorSupport);
463 464
}

465
EditorDocumentHandle *CppModelManager::editorDocument(const QString &filePath) const
466
{
467 468
    if (filePath.isEmpty())
        return 0;
469

470 471
    QMutexLocker locker(&d->m_cppEditorsMutex);
    return d->m_cppEditors.value(filePath, 0);
472 473
}

474
void CppModelManager::registerEditorDocument(EditorDocumentHandle *editorDocument)
475
{
476 477 478
    QTC_ASSERT(editorDocument, return);
    const QString filePath = editorDocument->filePath();
    QTC_ASSERT(!filePath.isEmpty(), return);
479

480 481 482
    QMutexLocker locker(&d->m_cppEditorsMutex);
    QTC_ASSERT(d->m_cppEditors.value(filePath, 0) == 0, return);
    d->m_cppEditors.insert(filePath, editorDocument);
483
}
484

485 486 487
void CppModelManager::unregisterEditorDocument(const QString &filePath)
{
    QTC_ASSERT(!filePath.isEmpty(), return);
488

489 490
    static short closedCppDocuments = 0;
    int openCppDocuments = 0;
491

492
    {
493 494 495 496
        QMutexLocker locker(&d->m_cppEditorsMutex);
        QTC_ASSERT(d->m_cppEditors.value(filePath, 0), return);
        QTC_CHECK(d->m_cppEditors.remove(filePath) == 1);
        openCppDocuments = d->m_cppEditors.size();
497 498
    }

499 500 501
    ++closedCppDocuments;
    if (openCppDocuments == 0 || closedCppDocuments == 5) {
        closedCppDocuments = 0;
502
        delayedGC();
503 504 505
    }
}

506
QList<int> CppModelManager::references(CPlusPlus::Symbol *symbol, const LookupContext &context)
507
{
508
    return d->m_findReferences->references(symbol, context);
509 510
}

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

517 518
void CppModelManager::renameUsages(CPlusPlus::Symbol *symbol,
                                   const CPlusPlus::LookupContext &context,
519
                                   const QString &replacement)
520 521
{
    if (symbol->identifier())
522
        d->m_findReferences->renameUsages(symbol, context, replacement);
Roberto Raggi's avatar
Roberto Raggi committed
523 524
}

Christian Kamm's avatar
Christian Kamm committed
525 526
void CppModelManager::findMacroUsages(const CPlusPlus::Macro &macro)
{
527
    d->m_findReferences->findMacroUses(macro);
Christian Kamm's avatar
Christian Kamm committed
528 529
}

530 531
void CppModelManager::renameMacroUsages(const CPlusPlus::Macro &macro, const QString &replacement)
{
532
    d->m_findReferences->renameMacroUses(macro, replacement);
533 534
}

535 536
void CppModelManager::replaceSnapshot(const CPlusPlus::Snapshot &newSnapshot)
{
537 538
    QMutexLocker snapshotLocker(&d->m_snapshotMutex);
    d->m_snapshot = newSnapshot;
539 540
}

541
WorkingCopy CppModelManager::buildWorkingCopyList()
con's avatar
con committed
542
{
543
    WorkingCopy workingCopy;
544

545 546
    foreach (const EditorDocumentHandle *cppEditor, cppEditors())
        workingCopy.insert(cppEditor->filePath(), cppEditor->contents(), cppEditor->revision());
con's avatar
con committed
547

548
    QSetIterator<AbstractEditorSupport *> it(d->m_extraEditorSupports);
549 550
    while (it.hasNext()) {
        AbstractEditorSupport *es = it.next();
551
        workingCopy.insert(es->fileName(), es->contents(), es->revision());
552 553
    }

554
    // Add the project configuration file
555
    QByteArray conf = codeModelConfiguration();
con's avatar
con committed
556
    conf += definedMacros();
557
    workingCopy.insert(configurationFileName(), conf);
con's avatar
con committed
558 559 560 561

    return workingCopy;
}

562
WorkingCopy CppModelManager::workingCopy() const
563 564 565 566
{
    return const_cast<CppModelManager *>(this)->buildWorkingCopyList();
}

567 568 569 570 571
QByteArray CppModelManager::codeModelConfiguration() const
{
    return QByteArray::fromRawData(pp_configuration, qstrlen(pp_configuration));
}

572
QFuture<void> CppModelManager::updateSourceFiles(const QSet<QString> &sourceFiles,
573
                                                 ProgressNotificationMode mode)
574
{
575
    if (sourceFiles.isEmpty() || !d->m_indexerEnabled)
576 577
        return QFuture<void>();

578 579 580
    if (d->m_indexingSupporter)
        d->m_indexingSupporter->refreshSourceFiles(sourceFiles, mode);
    return d->m_internalIndexingSupport->refreshSourceFiles(sourceFiles, mode);
581
}
con's avatar
con committed
582

583
QList<ProjectInfo> CppModelManager::projectInfos() const
584
{
585 586
    QMutexLocker locker(&d->m_projectMutex);
    return d->m_projectToProjectsInfo.values();
587 588
}

589
ProjectInfo CppModelManager::projectInfo(ProjectExplorer::Project *project) const
590
{
591 592
    QMutexLocker locker(&d->m_projectMutex);
    return d->m_projectToProjectsInfo.value(project, ProjectInfo(project));
593 594
}

595 596 597 598 599 600
/// \brief Remove all files and their includes (recursively) of given ProjectInfo from the snapshot.
void CppModelManager::removeProjectInfoFilesAndIncludesFromSnapshot(const ProjectInfo &projectInfo)
{
    if (!projectInfo.isValid())
        return;

601
    QMutexLocker snapshotLocker(&d->m_snapshotMutex);
602 603
    foreach (const ProjectPart::Ptr &projectPart, projectInfo.projectParts()) {
        foreach (const ProjectFile &cxxFile, projectPart->files) {
604 605 606
            foreach (const QString &fileName, d->m_snapshot.allIncludesForDocument(cxxFile.path))
                d->m_snapshot.remove(fileName);
            d->m_snapshot.remove(cxxFile.path);
607 608 609 610
        }
    }
}

611
QList<EditorDocumentHandle *> CppModelManager::cppEditors() const
612
{
613 614
    QMutexLocker locker(&d->m_cppEditorsMutex);
    return d->m_cppEditors.values();
615 616
}

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

class ProjectInfoComparer
627
{
628
public:
629 630
    ProjectInfoComparer(const ProjectInfo &oldProjectInfo,
                        const ProjectInfo &newProjectInfo)
631
        : m_old(oldProjectInfo)
632
        , m_oldSourceFiles(oldProjectInfo.sourceFiles())
633
        , m_new(newProjectInfo)
634
        , m_newSourceFiles(newProjectInfo.sourceFiles())
635 636
    {}

637 638 639
    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); }
640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668

    QSet<QString> addedFiles() const
    {
        QSet<QString> addedFilesSet = m_newSourceFiles;
        addedFilesSet.subtract(m_oldSourceFiles);
        return addedFilesSet;
    }

    QSet<QString> removedFiles() const
    {
        QSet<QString> removedFilesSet = m_oldSourceFiles;
        removedFilesSet.subtract(m_newSourceFiles);
        return removedFilesSet;
    }

    /// Returns a list of common files that have a changed timestamp.
    QSet<QString> timeStampModifiedFiles(const Snapshot &snapshot) const
    {
        QSet<QString> commonSourceFiles = m_newSourceFiles;
        commonSourceFiles.intersect(m_oldSourceFiles);

        QList<Document::Ptr> documentsToCheck;
        QSetIterator<QString> i(commonSourceFiles);
        while (i.hasNext()) {
            const QString file = i.next();
            if (Document::Ptr document = snapshot.document(file))
                documentsToCheck << document;
        }

669
        return CppModelManager::timeStampModifiedFiles(documentsToCheck);
670 671 672
    }

private:
673
    const ProjectInfo &m_old;
674 675
    const QSet<QString> m_oldSourceFiles;

676
    const ProjectInfo &m_new;
677 678 679
    const QSet<QString> m_newSourceFiles;
};

680 681 682
/// Make sure that m_projectMutex is locked when calling this.
void CppModelManager::recalculateFileToProjectParts()
{
683 684 685
    d->m_projectFileToProjectPart.clear();
    d->m_fileToProjectParts.clear();
    foreach (const ProjectInfo &projectInfo, d->m_projectToProjectsInfo) {
686
        foreach (const ProjectPart::Ptr &projectPart, projectInfo.projectParts()) {
687
            d->m_projectFileToProjectPart[projectPart->projectFile] = projectPart;
688
            foreach (const ProjectFile &cxxFile, projectPart->files)
689
                d->m_fileToProjectParts[cxxFile.path].append(projectPart);
690

691 692 693 694
        }
    }
}

695
void CppModelManager::updateCppEditorDocuments() const
696
{
697 698
    // Refresh visible documents
    QSet<Core::IDocument *> visibleCppEditorDocuments;
699
    foreach (Core::IEditor *editor, Core::EditorManager::visibleEditors()) {
700
        if (Core::IDocument *document = editor->document()) {
701
            if (EditorDocumentHandle *cppEditorDocument = editorDocument(document->filePath())) {
702
                visibleCppEditorDocuments.insert(document);
703
                cppEditorDocument->processor()->run();
704
            }
705 706 707
        }
    }

708 709 710 711 712
    // Mark invisible documents dirty
    QSet<Core::IDocument *> invisibleCppEditorDocuments
        = Core::DocumentModel::openedDocuments().toSet();
    invisibleCppEditorDocuments.subtract(visibleCppEditorDocuments);
    foreach (Core::IDocument *document, invisibleCppEditorDocuments) {
713 714
        if (EditorDocumentHandle *cppEditorDocument = editorDocument(document->filePath()))
            cppEditorDocument->setNeedsRefresh(true);
715 716 717
    }
}

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

723
    QSet<QString> filesToReindex;
724 725
    bool filesRemoved = false;

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

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

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

            if (!comparer.configurationOrFilesChanged()) {
                // Some other attached data might have changed
                d->m_projectToProjectsInfo.insert(project, newProjectInfo);
740
                return QFuture<void>();
741
            }
742

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

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

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

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

            // Announce and purge the removed files from the snapshot
            const QSet<QString> removedFiles = comparer.removedFiles();
            if (!removedFiles.isEmpty()) {
                filesRemoved = true;
                emit aboutToRemoveFiles(removedFiles.toList());
                removeFilesFromSnapshot(removedFiles);
            }

        // A new project was opened/created, do a full indexing
        } else {
773
            filesToReindex.unite(newSourceFiles);
774 775
        }

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

781 782 783
    } // Mutex scope

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

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

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

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

799 800
    // Trigger reindexing
    return updateSourceFiles(filesToReindex, ForcedProgressNotification);
801 802
}

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

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

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

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

825
    return parts.values();
826
}
con's avatar
con committed
827

828 829 830 831
ProjectPart::Ptr CppModelManager::fallbackProjectPart() const
{
    ProjectPart::Ptr part(new ProjectPart);

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

    return part;
}

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

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

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

858 859
void CppModelManager::delayedGC()
{