cppmodelmanager.cpp 33.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
    connect(Core::DocumentManager::instance(), &Core::DocumentManager::allDocumentsRenamed,
            this, &CppModelManager::renameIncludes);
con's avatar
con committed
305

306 307 308
    connect(Core::ICore::instance(), SIGNAL(coreAboutToClose()),
            this, SLOT(onCoreAboutToClose()));

con's avatar
con committed
309 310
    qRegisterMetaType<CPlusPlus::Document::Ptr>("CPlusPlus::Document::Ptr");

311
    d->m_modelManagerSupportFallback.reset(new ModelManagerSupportInternal);
312
    CppToolsPlugin::instance()->codeModelSettings()->setDefaultId(
313 314
                d->m_modelManagerSupportFallback->id());
    addModelManagerSupport(d->m_modelManagerSupportFallback.data());
315

316
    d->m_internalIndexingSupport = new BuiltinIndexingSupport;
con's avatar
con committed
317 318 319
}

CppModelManager::~CppModelManager()
320
{
321
    delete d->m_internalIndexingSupport;
322
    delete d;
323
}
con's avatar
con committed
324

325
Snapshot CppModelManager::snapshot() const
326
{
327 328
    QMutexLocker locker(&d->m_snapshotMutex);
    return d->m_snapshot;
329
}
con's avatar
con committed
330

331 332
Document::Ptr CppModelManager::document(const QString &fileName) const
{
333 334
    QMutexLocker locker(&d->m_snapshotMutex);
    return d->m_snapshot.document(fileName);
335 336 337 338 339 340 341
}

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

344
    Document::Ptr previous = d->m_snapshot.document(newDoc->fileName());
345 346 347 348
    if (previous && (newDoc->revision() != 0 && newDoc->revision() < previous->revision()))
        // the new document is outdated
        return false;

349
    d->m_snapshot.insert(newDoc);
350 351 352
    return true;
}

353 354
void CppModelManager::ensureUpdated()
{
355 356
    QMutexLocker locker(&d->m_projectMutex);
    if (!d->m_dirty)
357 358
        return;

359 360 361 362
    d->m_projectFiles = internalProjectFiles();
    d->m_headerPaths = internalHeaderPaths();
    d->m_definedMacros = internalDefinedMacros();
    d->m_dirty = false;
363 364
}

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

381
ProjectPart::HeaderPaths CppModelManager::internalHeaderPaths() const
con's avatar
con committed
382
{
383
    ProjectPart::HeaderPaths headerPaths;
384
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(d->m_projectToProjectsInfo);
con's avatar
con committed
385 386
    while (it.hasNext()) {
        it.next();
387
        const ProjectInfo pinfo = it.value();
388 389 390 391 392 393 394 395
        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
396
    }
397
    return headerPaths;
con's avatar
con committed
398 399
}

400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415
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);
        }
    }
}

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

434
/// This function will acquire mutexes!
435
void CppModelManager::dumpModelManagerConfiguration(const QString &logFileId)
436
{
437 438 439 440
    const Snapshot globalSnapshot = snapshot();
    const QString globalSnapshotTitle
        = QString::fromLatin1("Global/Indexing Snapshot (%1 Documents)").arg(globalSnapshot.size());

441
    CppCodeModelInspector::Dumper dumper(globalSnapshot, logFileId);
442 443 444
    dumper.dumpProjectInfos(projectInfos());
    dumper.dumpSnapshot(globalSnapshot, globalSnapshotTitle, /*isGlobalSnapshot=*/ true);
    dumper.dumpWorkingCopy(workingCopy());
445
    ensureUpdated();
446
    dumper.dumpMergedEntities(d->m_headerPaths, d->m_definedMacros);
447 448
}

449
void CppModelManager::addExtraEditorSupport(AbstractEditorSupport *editorSupport)
450
{
451
    d->m_extraEditorSupports.insert(editorSupport);
452 453
}

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

459
EditorDocumentHandle *CppModelManager::editorDocument(const QString &filePath) const
460
{
461
    QTC_ASSERT(!filePath.isEmpty(), return 0);
462

463 464
    QMutexLocker locker(&d->m_cppEditorsMutex);
    return d->m_cppEditors.value(filePath, 0);
465 466
}

467
void CppModelManager::registerEditorDocument(EditorDocumentHandle *editorDocument)
468
{
469 470 471
    QTC_ASSERT(editorDocument, return);
    const QString filePath = editorDocument->filePath();
    QTC_ASSERT(!filePath.isEmpty(), return);
472

473 474 475
    QMutexLocker locker(&d->m_cppEditorsMutex);
    QTC_ASSERT(d->m_cppEditors.value(filePath, 0) == 0, return);
    d->m_cppEditors.insert(filePath, editorDocument);
476
}
477

478 479 480
void CppModelManager::unregisterEditorDocument(const QString &filePath)
{
    QTC_ASSERT(!filePath.isEmpty(), return);
481

482 483
    static short closedCppDocuments = 0;
    int openCppDocuments = 0;
484

485
    {
486 487 488 489
        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();
490 491
    }

492 493 494
    ++closedCppDocuments;
    if (openCppDocuments == 0 || closedCppDocuments == 5) {
        closedCppDocuments = 0;
495
        delayedGC();
496 497 498
    }
}

499
QList<int> CppModelManager::references(CPlusPlus::Symbol *symbol, const LookupContext &context)
500
{
501
    return d->m_findReferences->references(symbol, context);
502 503
}

504
void CppModelManager::findUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context)
Roberto Raggi's avatar
Roberto Raggi committed
505
{
506
    if (symbol->identifier())
507
        d->m_findReferences->findUsages(symbol, context);
508 509
}

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

Christian Kamm's avatar
Christian Kamm committed
518 519
void CppModelManager::findMacroUsages(const CPlusPlus::Macro &macro)
{
520
    d->m_findReferences->findMacroUses(macro);
Christian Kamm's avatar
Christian Kamm committed
521 522
}

523 524
void CppModelManager::renameMacroUsages(const CPlusPlus::Macro &macro, const QString &replacement)
{
525
    d->m_findReferences->renameMacroUses(macro, replacement);
526 527
}

528 529
void CppModelManager::replaceSnapshot(const CPlusPlus::Snapshot &newSnapshot)
{
530 531
    QMutexLocker snapshotLocker(&d->m_snapshotMutex);
    d->m_snapshot = newSnapshot;
532 533
}

534
WorkingCopy CppModelManager::buildWorkingCopyList()
con's avatar
con committed
535
{
536
    WorkingCopy workingCopy;
537

538 539
    foreach (const EditorDocumentHandle *cppEditor, cppEditors())
        workingCopy.insert(cppEditor->filePath(), cppEditor->contents(), cppEditor->revision());
con's avatar
con committed
540

541
    QSetIterator<AbstractEditorSupport *> it(d->m_extraEditorSupports);
542 543
    while (it.hasNext()) {
        AbstractEditorSupport *es = it.next();
544
        workingCopy.insert(es->fileName(), es->contents(), es->revision());
545 546
    }

547
    // Add the project configuration file
548
    QByteArray conf = codeModelConfiguration();
con's avatar
con committed
549
    conf += definedMacros();
550
    workingCopy.insert(configurationFileName(), conf);
con's avatar
con committed
551 552 553 554

    return workingCopy;
}

555
WorkingCopy CppModelManager::workingCopy() const
556 557 558 559
{
    return const_cast<CppModelManager *>(this)->buildWorkingCopyList();
}

560 561 562 563 564
QByteArray CppModelManager::codeModelConfiguration() const
{
    return QByteArray::fromRawData(pp_configuration, qstrlen(pp_configuration));
}

565
QFuture<void> CppModelManager::updateSourceFiles(const QSet<QString> &sourceFiles,
566
                                                 ProgressNotificationMode mode)
567
{
568
    if (sourceFiles.isEmpty() || !d->m_indexerEnabled)
569 570
        return QFuture<void>();

571 572 573
    if (d->m_indexingSupporter)
        d->m_indexingSupporter->refreshSourceFiles(sourceFiles, mode);
    return d->m_internalIndexingSupport->refreshSourceFiles(sourceFiles, mode);
574
}
con's avatar
con committed
575

576
QList<ProjectInfo> CppModelManager::projectInfos() const
577
{
578 579
    QMutexLocker locker(&d->m_projectMutex);
    return d->m_projectToProjectsInfo.values();
580 581
}

582
ProjectInfo CppModelManager::projectInfo(ProjectExplorer::Project *project) const
583
{
584 585
    QMutexLocker locker(&d->m_projectMutex);
    return d->m_projectToProjectsInfo.value(project, ProjectInfo(project));
586 587
}

588 589 590 591 592 593
/// \brief Remove all files and their includes (recursively) of given ProjectInfo from the snapshot.
void CppModelManager::removeProjectInfoFilesAndIncludesFromSnapshot(const ProjectInfo &projectInfo)
{
    if (!projectInfo.isValid())
        return;

594
    QMutexLocker snapshotLocker(&d->m_snapshotMutex);
595 596
    foreach (const ProjectPart::Ptr &projectPart, projectInfo.projectParts()) {
        foreach (const ProjectFile &cxxFile, projectPart->files) {
597 598 599
            foreach (const QString &fileName, d->m_snapshot.allIncludesForDocument(cxxFile.path))
                d->m_snapshot.remove(fileName);
            d->m_snapshot.remove(cxxFile.path);
600 601 602 603
        }
    }
}

604
QList<EditorDocumentHandle *> CppModelManager::cppEditors() const
605
{
606 607
    QMutexLocker locker(&d->m_cppEditorsMutex);
    return d->m_cppEditors.values();
608 609
}

610 611 612
/// \brief Remove all given files from the snapshot.
void CppModelManager::removeFilesFromSnapshot(const QSet<QString> &filesToRemove)
{
613
    QMutexLocker snapshotLocker(&d->m_snapshotMutex);
614 615
    QSetIterator<QString> i(filesToRemove);
    while (i.hasNext())
616
        d->m_snapshot.remove(i.next());
617 618 619
}

class ProjectInfoComparer
620
{
621
public:
622 623
    ProjectInfoComparer(const ProjectInfo &oldProjectInfo,
                        const ProjectInfo &newProjectInfo)
624
        : m_old(oldProjectInfo)
625
        , m_oldSourceFiles(oldProjectInfo.sourceFiles())
626
        , m_new(newProjectInfo)
627
        , m_newSourceFiles(newProjectInfo.sourceFiles())
628 629 630 631 632 633 634 635 636 637
    {}

    bool definesChanged() const
    {
        return m_new.defines() != m_old.defines();
    }

    bool configurationChanged() const
    {
        return definesChanged()
638
            || m_new.headerPaths() != m_old.headerPaths();
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 668 669 670 671 672 673
    }

    bool nothingChanged() const
    {
        return !configurationChanged() && m_new.sourceFiles() == m_old.sourceFiles();
    }

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

674
        return CppModelManager::timeStampModifiedFiles(documentsToCheck);
675 676 677
    }

private:
678
    const ProjectInfo &m_old;
679 680
    const QSet<QString> m_oldSourceFiles;

681
    const ProjectInfo &m_new;
682 683 684
    const QSet<QString> m_newSourceFiles;
};

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

696 697 698 699
        }
    }
}

700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717
void CppModelManager::updateVisibleEditorDocuments() const
{
    QSet<QString> visibleDocumentsInEditMode;
    foreach (Core::IEditor *editor, Core::EditorManager::visibleEditors()) {
        if (const Core::IDocument *document = editor->document()) {
            const QString filePath = document->filePath();
            QTC_ASSERT(!filePath.isEmpty(), continue);
            visibleDocumentsInEditMode.insert(filePath);
        }
    }

    // Re-process these documents
    foreach (const QString &filePath, visibleDocumentsInEditMode) {
        if (EditorDocumentHandle *editor = editorDocument(filePath))
            editor->processor()->run();
    }
}

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 736
            ProjectInfoComparer comparer(oldProjectInfo, newProjectInfo);
            if (comparer.nothingChanged())
737 738
                return QFuture<void>();

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

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

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

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

            // 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 {
769
            filesToReindex.unite(newSourceFiles);
770 771
        }

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

777 778 779
    } // Mutex scope

    // If requested, dump everything we got
780
    if (DumpProjectInfo)
781
        dumpModelManagerConfiguration(QLatin1String("updateProjectInfo"));
782

783 784 785 786 787
    // Remove files from snapshot that are not reachable any more
    if (filesRemoved)
        GC();

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

789 790 791 792 793 794
    // 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.
    updateVisibleEditorDocuments();

795 796
    // Trigger reindexing
    return updateSourceFiles(filesToReindex, ForcedProgressNotification);
797 798
}

799 800
ProjectPart::Ptr CppModelManager::projectPartForProjectFile(const QString &projectFile) const
{
801
    return d->m_projectFileToProjectPart.value(projectFile);
802 803
}

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

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

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

821
    return parts.values();
822
}
con's avatar
con committed
823

824 825 826 827
ProjectPart::Ptr CppModelManager::fallbackProjectPart() const
{
    ProjectPart::Ptr part(new ProjectPart);

828 829
    part->projectDefines = d->m_definedMacros;
    part->headerPaths = d->m_headerPaths;
830 831
    part->languageVersion = ProjectPart::CXX14;
    part->languageExtensions = ProjectPart::AllExtensions;
832 833 834 835 836
    part->qtVersion = ProjectPart::Qt5;

    return part;
}

con's avatar
con committed
837 838
bool CppModelManager::isCppEditor(Core::IEditor *editor) const
{
839
    return editor->context().contains(ProjectExplorer::Constants::LANG_CXX);
con's avatar
con committed
840 841 842 843
}

void CppModelManager::emitDocumentUpdated(Document::Ptr doc)
{
844
    if (replaceDocument(doc))
845
        emit documentUpdated(doc);
Roberto Raggi's avatar
Roberto Raggi committed
846 847
}

848 849
void CppModelManager::onProjectAdded(ProjectExplorer::Project *)
{
850 851
    QMutexLocker locker(&d->m_projectMutex);
    d->m_dirty = true;
852 853
}

854 855
void CppModelManager::delayedGC()
{
856 857
    if (d->m_enableGC)
        d->m_delayedGcTimer.start(500);
858 859
}

con's avatar
con committed
860 861
void CppModelManager::onAboutToRemoveProject(ProjectExplorer::Project *project)
{
862
    do {
863 864 865
        QMutexLocker locker(&d->m_projectMutex);
        d->m_dirty = true;
        d->m_projectToProjectsInfo.remove(project);
866
        recalculateFileToProjectParts();
867 868
    } while (0);

869 870 871
    delayedGC();
}

872 873 874 875 876 877 878 879
void CppModelManager::onSourceFilesRefreshed() const
{
    if (BuiltinIndexingSupport::isFindErrorsIndexingActive()) {
        QTimer::singleShot(1, QCoreApplication::instance(), SLOT(quit()));
        qDebug("FindErrorsIndexing: Done, requesting Qt Creator to quit.");
    }
}

880 881
void CppModelManager::onAboutToLoadSession()
{
882 883
    if (d->m_delayedGcTimer.isActive())
        d->m_delayedGcTimer.stop();
con's avatar
con committed
884 885 886
    GC();
}

887
void CppModelManager::onAboutToUnloadSession()
con's avatar
con committed
888
{
889
    Core::ProgressManager::cancelTasks(CppTools::Constants::TASK_INDEX);
890
    do {
891 892
        QMutexLocker locker(&d->m_projectMutex);
        d->m_projectToProjectsInfo.clear();
893
        recalculateFileToProjectParts();
894
        d->m_dirty = true;
895
    } while (0);
con's avatar
con committed
896 897
}

898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926
void CppModelManager::renameIncludes(const QString &oldFileName, const QString &newFileName)
{
    if (oldFileName.isEmpty() || newFileName.isEmpty())
        return;

    const QFileInfo oldFileInfo(oldFileName);
    const QFileInfo newFileInfo(newFileName);

    // We just want to handle renamings so return when the file was actually moved.
    if (oldFileInfo.absoluteDir() != newFileInfo.absoluteDir())
        return;

    const TextEditor::RefactoringChanges changes;

    foreach (Snapshot::IncludeLocation loc, snapshot().includeLocationsOfDocument(oldFileName)) {
        TextEditor::RefactoringFilePtr file = changes.file(loc.first->fileName());
        const QTextBlock &block = file->document()->findBlockByLineNumber(loc.second - 1);
        const int replaceStart = block.text().indexOf(oldFileInfo.fileName());
        if (replaceStart > -1) {
            Utils::ChangeSet changeSet;
            changeSet.replace(block.position() + replaceStart,
                              block.position() + replaceStart + oldFileInfo.fileName().length(),
                              newFileInfo.fileName());
            file->setChangeSet(changeSet);
            file->apply();
        }
    }
}

927 928
void CppModelManager::onCoreAboutToClose()
{
929
    d->m_enableGC = false;
930 931
}

con's avatar
con committed
932 933
void CppModelManager::GC()
{
hjk's avatar