cppmodelmanager.cpp 34.8 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
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
            foreach (const ProjectPart::HeaderPath &path, part->headerPaths) {
396
                const ProjectPart::HeaderPath hp(QDir::cleanPath(path.path), path.type);
397 398 399 400
                if (!headerPaths.contains(hp))
                    headerPaths += hp;
            }
        }
con's avatar
con committed
401
    }
402
    return headerPaths;
con's avatar
con committed
403 404
}

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

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

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

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

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

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

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

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

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

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

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

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

491
    {
492 493 494 495
        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();
496 497
    }

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

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

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

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

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

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

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

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

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

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

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

    return workingCopy;
}

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

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

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

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

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

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

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

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

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

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

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

636 637 638
    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); }
639 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

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

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

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

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

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

690 691 692 693
        }
    }
}

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

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

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

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

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

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

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

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

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

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

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

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

            // 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 {
772
            filesToReindex.unite(newSourceFiles);
773 774
        }

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

780 781 782
    } // Mutex scope

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

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

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

792 793 794 795
    // 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.
796
    updateCppEditorDocuments();
797

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

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

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

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

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

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

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