cppmodelmanager.cpp 35.8 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
Eike Ziller's avatar
Eike Ziller committed
3 4
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
con's avatar
con committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
con's avatar
con committed
7
**
hjk's avatar
hjk committed
8 9 10 11
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
Eike Ziller's avatar
Eike Ziller committed
12 13
** a written agreement between you and The Qt Company.  For licensing terms and
** conditions see http://www.qt.io/terms-conditions.  For further information
Eike Ziller's avatar
Eike Ziller committed
14
** 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
**
Eike Ziller's avatar
Eike Ziller committed
25 26
** In addition, as a special exception, The Qt Company gives you certain additional
** rights.  These rights are described in The Qt Company 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 "baseeditordocumentprocessor.h"
35
#include "builtinindexingsupport.h"
36
#include "cppcodemodelinspectordumper.h"
37
#include "cppcodemodelsettings.h"
38
#include "cppfindreferences.h"
39
#include "cppindexingsupport.h"
40
#include "cppmodelmanagersupportinternal.h"
41
#include "cpprefactoringchanges.h"
42
#include "cppsourceprocessor.h"
43
#include "cpptoolsconstants.h"
44
#include "cpptoolsplugin.h"
45
#include "editordocumenthandle.h"
con's avatar
con committed
46

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

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

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

72 73
Q_DECLARE_METATYPE(QSet<QString>)

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

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

80
#ifdef QTCREATOR_WITH_DUMP_AST
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119

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

122 123 124 125 126 127 128 129 130 131 132
namespace CppTools {
namespace Internal {

static QMutex m_instanceMutex;
static CppModelManager *m_instance;

class CppModelManagerPrivate
{
public:
    // Snapshot
    mutable QMutex m_snapshotMutex;
133
    Snapshot m_snapshot;
134 135 136 137

    // Project integration
    mutable QMutex m_projectMutex;
    QMap<ProjectExplorer::Project *, ProjectInfo> m_projectToProjectsInfo;
138 139
    QMap<Utils::FileName, QList<ProjectPart::Ptr> > m_fileToProjectParts;
    QMap<QString, ProjectPart::Ptr> m_projectFileToProjectPart;
140 141 142 143 144 145 146
    // 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
147 148
    mutable QMutex m_cppEditorDocumentsMutex;
    QMap<QString, CppEditorDocumentHandle *> m_cppEditorDocuments;
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
    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
169
    "# 1 \"<configuration>\"\n"
170
    "#define Q_CREATOR_RUN 1\n"
con's avatar
con committed
171 172 173 174 175 176
    "#define __cplusplus 1\n"
    "#define __extension__\n"
    "#define __context__\n"
    "#define __range__\n"
    "#define   restrict\n"
    "#define __restrict\n"
177
    "#define __restrict__\n"
con's avatar
con committed
178

179 180 181 182
    "#define __complex__\n"
    "#define __imag__\n"
    "#define __real__\n"

183 184
    "#define __builtin_va_arg(a,b) ((b)0)\n"

185 186
    "#define _Pragma(x)\n" // C99 _Pragma operator

con's avatar
con committed
187 188
    // ### add macros for win32
    "#define __cdecl\n"
189
    "#define __stdcall\n"
190
    "#define __thiscall\n"
con's avatar
con committed
191 192 193 194 195
    "#define QT_WA(x) x\n"
    "#define CALLBACK\n"
    "#define STDMETHODCALLTYPE\n"
    "#define __RPC_FAR\n"
    "#define __declspec(a)\n"
196 197 198
    "#define STDMETHOD(method) virtual HRESULT STDMETHODCALLTYPE method\n"
    "#define __try try\n"
    "#define __except catch\n"
199 200
    "#define __finally\n"
    "#define __inline inline\n"
201 202
    "#define __forceinline inline\n"
    "#define __pragma(x)\n";
con's avatar
con committed
203

204
QSet<QString> CppModelManager::timeStampModifiedFiles(const QList<Document::Ptr> &documentsToCheck)
205
{
206
    QSet<QString> sourceFiles;
207

208
    foreach (const Document::Ptr doc, documentsToCheck) {
209 210
        const QDateTime lastModified = doc->lastModified();

211
        if (!lastModified.isNull()) {
212 213 214
            QFileInfo fileInfo(doc->fileName());

            if (fileInfo.exists() && fileInfo.lastModified() != lastModified)
215
                sourceFiles.insert(doc->fileName());
216 217 218
        }
    }

219 220 221
    return sourceFiles;
}

222 223 224 225 226 227 228 229 230 231 232 233
/*!
 * \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
234
    return new CppSourceProcessor(that->snapshot(), [that](const Document::Ptr &doc) {
235 236 237 238 239
        const Document::Ptr previousDocument = that->document(doc->fileName());
        const unsigned newRevision = previousDocument.isNull()
                ? 1U
                : previousDocument->revision() + 1;
        doc->setRevision(newRevision);
240 241 242 243 244
        that->emitDocumentUpdated(doc);
        doc->releaseSourceAndAST();
    });
}

245 246 247 248 249 250 251
QString CppModelManager::editorConfigurationFileName()
{
    return QLatin1String("<per-editor-defines>");
}

QString CppModelManager::configurationFileName()
{
252
    return Preprocessor::configurationFileName;
253 254
}

255 256 257 258 259 260 261
void CppModelManager::updateModifiedSourceFiles()
{
    const Snapshot snapshot = this->snapshot();
    QList<Document::Ptr> documentsToCheck;
    foreach (const Document::Ptr document, snapshot)
        documentsToCheck << document;

262
    updateSourceFiles(timeStampModifiedFiles(documentsToCheck));
263 264
}

con's avatar
con committed
265 266
/*!
    \class CppTools::CppModelManager
267 268 269
    \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
270

271 272 273 274
    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
275 276
*/

277 278
CppModelManager *CppModelManager::instance()
{
279 280 281 282 283 284 285 286
    if (m_instance)
        return m_instance;

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

    return m_instance;
287 288
}

289
CppModelManager::CppModelManager(QObject *parent)
290
    : CppModelManagerBase(parent), d(new CppModelManagerPrivate)
con's avatar
con committed
291
{
292 293 294
    d->m_indexingSupporter = 0;
    d->m_enableGC = true;

295
    qRegisterMetaType<QSet<QString> >();
296
    connect(this, SIGNAL(sourceFilesRefreshed(QSet<QString>)),
297
            this, SLOT(onSourceFilesRefreshed()));
Erik Verbruggen's avatar
Erik Verbruggen committed
298

299 300
    d->m_findReferences = new CppFindReferences(this);
    d->m_indexerEnabled = qgetenv("QTC_NO_CODE_INDEXER") != "1";
Roberto Raggi's avatar
Roberto Raggi committed
301

302
    d->m_dirty = true;
303

304 305 306
    d->m_delayedGcTimer.setObjectName(QLatin1String("CppModelManager::m_delayedGcTimer"));
    d->m_delayedGcTimer.setSingleShot(true);
    connect(&d->m_delayedGcTimer, SIGNAL(timeout()), this, SLOT(GC()));
307

hjk's avatar
hjk committed
308 309
    QObject *sessionManager = ProjectExplorer::SessionManager::instance();
    connect(sessionManager, SIGNAL(projectAdded(ProjectExplorer::Project*)),
310
            this, SLOT(onProjectAdded(ProjectExplorer::Project*)));
hjk's avatar
hjk committed
311
    connect(sessionManager, SIGNAL(aboutToRemoveProject(ProjectExplorer::Project*)),
Robert Loehning's avatar
Robert Loehning committed
312
            this, SLOT(onAboutToRemoveProject(ProjectExplorer::Project*)));
hjk's avatar
hjk committed
313
    connect(sessionManager, SIGNAL(aboutToLoadSession(QString)),
314
            this, SLOT(onAboutToLoadSession()));
hjk's avatar
hjk committed
315
    connect(sessionManager, SIGNAL(aboutToUnloadSession(QString)),
316
            this, SLOT(onAboutToUnloadSession()));
317 318 319 320

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

321 322
    connect(Core::DocumentManager::instance(), &Core::DocumentManager::allDocumentsRenamed,
            this, &CppModelManager::renameIncludes);
con's avatar
con committed
323

324 325 326
    connect(Core::ICore::instance(), SIGNAL(coreAboutToClose()),
            this, SLOT(onCoreAboutToClose()));

con's avatar
con committed
327
    qRegisterMetaType<CPlusPlus::Document::Ptr>("CPlusPlus::Document::Ptr");
328
    qRegisterMetaType<QList<Document::DiagnosticMessage>>(
329
                "QList<CPlusPlus::Document::DiagnosticMessage>");
con's avatar
con committed
330

331
    d->m_modelManagerSupportFallback.reset(new ModelManagerSupportInternal);
332
    CppToolsPlugin::instance()->codeModelSettings()->setDefaultId(
333 334
                d->m_modelManagerSupportFallback->id());
    addModelManagerSupport(d->m_modelManagerSupportFallback.data());
335

336
    d->m_internalIndexingSupport = new BuiltinIndexingSupport;
con's avatar
con committed
337 338 339
}

CppModelManager::~CppModelManager()
340
{
341
    delete d->m_internalIndexingSupport;
342
    delete d;
343
}
con's avatar
con committed
344

345
Snapshot CppModelManager::snapshot() const
346
{
347 348
    QMutexLocker locker(&d->m_snapshotMutex);
    return d->m_snapshot;
349
}
con's avatar
con committed
350

351 352
Document::Ptr CppModelManager::document(const QString &fileName) const
{
353 354
    QMutexLocker locker(&d->m_snapshotMutex);
    return d->m_snapshot.document(fileName);
355 356 357 358 359 360 361
}

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

364
    Document::Ptr previous = d->m_snapshot.document(newDoc->fileName());
365 366 367 368
    if (previous && (newDoc->revision() != 0 && newDoc->revision() < previous->revision()))
        // the new document is outdated
        return false;

369
    d->m_snapshot.insert(newDoc);
370 371 372
    return true;
}

373 374
void CppModelManager::ensureUpdated()
{
375 376
    QMutexLocker locker(&d->m_projectMutex);
    if (!d->m_dirty)
377 378
        return;

379 380 381 382
    d->m_projectFiles = internalProjectFiles();
    d->m_headerPaths = internalHeaderPaths();
    d->m_definedMacros = internalDefinedMacros();
    d->m_dirty = false;
383 384
}

385
QStringList CppModelManager::internalProjectFiles() const
con's avatar
con committed
386 387
{
    QStringList files;
388
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(d->m_projectToProjectsInfo);
con's avatar
con committed
389 390
    while (it.hasNext()) {
        it.next();
391
        const ProjectInfo pinfo = it.value();
392
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
393 394
            foreach (const ProjectFile &file, part->files)
                files += file.path;
395
        }
con's avatar
con committed
396
    }
397
    files.removeDuplicates();
con's avatar
con committed
398 399 400
    return files;
}

401
ProjectPart::HeaderPaths CppModelManager::internalHeaderPaths() const
con's avatar
con committed
402
{
403
    ProjectPart::HeaderPaths headerPaths;
404
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(d->m_projectToProjectsInfo);
con's avatar
con committed
405 406
    while (it.hasNext()) {
        it.next();
407
        const ProjectInfo pinfo = it.value();
408 409
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
            foreach (const ProjectPart::HeaderPath &path, part->headerPaths) {
410
                const ProjectPart::HeaderPath hp(QDir::cleanPath(path.path), path.type);
411 412 413 414
                if (!headerPaths.contains(hp))
                    headerPaths += hp;
            }
        }
con's avatar
con committed
415
    }
416
    return headerPaths;
con's avatar
con committed
417 418
}

419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434
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);
        }
    }
}

435
QByteArray CppModelManager::internalDefinedMacros() const
con's avatar
con committed
436 437
{
    QByteArray macros;
438
    QSet<QByteArray> alreadyIn;
439
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(d->m_projectToProjectsInfo);
con's avatar
con committed
440 441
    while (it.hasNext()) {
        it.next();
442
        const ProjectInfo pinfo = it.value();
443
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
444 445
            addUnique(part->toolchainDefines.split('\n'), &macros, &alreadyIn);
            addUnique(part->projectDefines.split('\n'), &macros, &alreadyIn);
446
            if (!part->projectConfigFile.isEmpty())
447
                macros += ProjectPart::readProjectConfigFile(part);
448
        }
con's avatar
con committed
449 450 451 452
    }
    return macros;
}

453
/// This function will acquire mutexes!
454
void CppModelManager::dumpModelManagerConfiguration(const QString &logFileId)
455
{
456 457 458 459
    const Snapshot globalSnapshot = snapshot();
    const QString globalSnapshotTitle
        = QString::fromLatin1("Global/Indexing Snapshot (%1 Documents)").arg(globalSnapshot.size());

460
    CppCodeModelInspector::Dumper dumper(globalSnapshot, logFileId);
461 462 463
    dumper.dumpProjectInfos(projectInfos());
    dumper.dumpSnapshot(globalSnapshot, globalSnapshotTitle, /*isGlobalSnapshot=*/ true);
    dumper.dumpWorkingCopy(workingCopy());
464
    ensureUpdated();
465
    dumper.dumpMergedEntities(d->m_headerPaths, d->m_definedMacros);
466 467
}

468
void CppModelManager::addExtraEditorSupport(AbstractEditorSupport *editorSupport)
469
{
470
    d->m_extraEditorSupports.insert(editorSupport);
471 472
}

473
void CppModelManager::removeExtraEditorSupport(AbstractEditorSupport *editorSupport)
474
{
475
    d->m_extraEditorSupports.remove(editorSupport);
476 477
}

478
CppEditorDocumentHandle *CppModelManager::cppEditorDocument(const QString &filePath) const
479
{
480 481
    if (filePath.isEmpty())
        return 0;
482

483 484
    QMutexLocker locker(&d->m_cppEditorDocumentsMutex);
    return d->m_cppEditorDocuments.value(filePath, 0);
485 486
}

487
void CppModelManager::registerCppEditorDocument(CppEditorDocumentHandle *editorDocument)
488
{
489 490 491
    QTC_ASSERT(editorDocument, return);
    const QString filePath = editorDocument->filePath();
    QTC_ASSERT(!filePath.isEmpty(), return);
492

493 494 495
    QMutexLocker locker(&d->m_cppEditorDocumentsMutex);
    QTC_ASSERT(d->m_cppEditorDocuments.value(filePath, 0) == 0, return);
    d->m_cppEditorDocuments.insert(filePath, editorDocument);
496
}
497

498
void CppModelManager::unregisterCppEditorDocument(const QString &filePath)
499 500
{
    QTC_ASSERT(!filePath.isEmpty(), return);
501

502 503
    static short closedCppDocuments = 0;
    int openCppDocuments = 0;
504

505
    {
506 507 508 509
        QMutexLocker locker(&d->m_cppEditorDocumentsMutex);
        QTC_ASSERT(d->m_cppEditorDocuments.value(filePath, 0), return);
        QTC_CHECK(d->m_cppEditorDocuments.remove(filePath) == 1);
        openCppDocuments = d->m_cppEditorDocuments.size();
510 511
    }

512 513 514
    ++closedCppDocuments;
    if (openCppDocuments == 0 || closedCppDocuments == 5) {
        closedCppDocuments = 0;
515
        delayedGC();
516 517 518
    }
}

519
QList<int> CppModelManager::references(Symbol *symbol, const LookupContext &context)
520
{
521
    return d->m_findReferences->references(symbol, context);
522 523
}

524
void CppModelManager::findUsages(Symbol *symbol, const LookupContext &context)
Roberto Raggi's avatar
Roberto Raggi committed
525
{
526
    if (symbol->identifier())
527
        d->m_findReferences->findUsages(symbol, context);
528 529
}

530 531
void CppModelManager::renameUsages(Symbol *symbol,
                                   const LookupContext &context,
532
                                   const QString &replacement)
533 534
{
    if (symbol->identifier())
535
        d->m_findReferences->renameUsages(symbol, context, replacement);
Roberto Raggi's avatar
Roberto Raggi committed
536 537
}

538
void CppModelManager::findMacroUsages(const Macro &macro)
Christian Kamm's avatar
Christian Kamm committed
539
{
540
    d->m_findReferences->findMacroUses(macro);
Christian Kamm's avatar
Christian Kamm committed
541 542
}

543
void CppModelManager::renameMacroUsages(const Macro &macro, const QString &replacement)
544
{
545
    d->m_findReferences->renameMacroUses(macro, replacement);
546 547
}

548
void CppModelManager::replaceSnapshot(const Snapshot &newSnapshot)
549
{
550 551
    QMutexLocker snapshotLocker(&d->m_snapshotMutex);
    d->m_snapshot = newSnapshot;
552 553
}

554
WorkingCopy CppModelManager::buildWorkingCopyList()
con's avatar
con committed
555
{
556
    WorkingCopy workingCopy;
557

558 559 560 561 562
    foreach (const CppEditorDocumentHandle *cppEditorDocument, cppEditorDocuments()) {
        workingCopy.insert(cppEditorDocument->filePath(),
                           cppEditorDocument->contents(),
                           cppEditorDocument->revision());
    }
con's avatar
con committed
563

564
    QSetIterator<AbstractEditorSupport *> it(d->m_extraEditorSupports);
565 566
    while (it.hasNext()) {
        AbstractEditorSupport *es = it.next();
567
        workingCopy.insert(es->fileName(), es->contents(), es->revision());
568 569
    }

570
    // Add the project configuration file
571
    QByteArray conf = codeModelConfiguration();
con's avatar
con committed
572
    conf += definedMacros();
573
    workingCopy.insert(configurationFileName(), conf);
con's avatar
con committed
574 575 576 577

    return workingCopy;
}

578
WorkingCopy CppModelManager::workingCopy() const
579 580 581 582
{
    return const_cast<CppModelManager *>(this)->buildWorkingCopyList();
}

583 584 585 586 587
QByteArray CppModelManager::codeModelConfiguration() const
{
    return QByteArray::fromRawData(pp_configuration, qstrlen(pp_configuration));
}

588
QFuture<void> CppModelManager::updateSourceFiles(const QSet<QString> &sourceFiles,
589
                                                 ProgressNotificationMode mode)
590
{
591
    if (sourceFiles.isEmpty() || !d->m_indexerEnabled)
592 593
        return QFuture<void>();

594 595 596
    if (d->m_indexingSupporter)
        d->m_indexingSupporter->refreshSourceFiles(sourceFiles, mode);
    return d->m_internalIndexingSupport->refreshSourceFiles(sourceFiles, mode);
597
}
con's avatar
con committed
598

599
QList<ProjectInfo> CppModelManager::projectInfos() const
600
{
601 602
    QMutexLocker locker(&d->m_projectMutex);
    return d->m_projectToProjectsInfo.values();
603 604
}

605
ProjectInfo CppModelManager::projectInfo(ProjectExplorer::Project *project) const
606
{
607
    QMutexLocker locker(&d->m_projectMutex);
608
    return d->m_projectToProjectsInfo.value(project, ProjectInfo());
609 610
}

611 612 613 614 615 616
/// \brief Remove all files and their includes (recursively) of given ProjectInfo from the snapshot.
void CppModelManager::removeProjectInfoFilesAndIncludesFromSnapshot(const ProjectInfo &projectInfo)
{
    if (!projectInfo.isValid())
        return;

617
    QMutexLocker snapshotLocker(&d->m_snapshotMutex);
618 619
    foreach (const ProjectPart::Ptr &projectPart, projectInfo.projectParts()) {
        foreach (const ProjectFile &cxxFile, projectPart->files) {
620 621 622
            foreach (const QString &fileName, d->m_snapshot.allIncludesForDocument(cxxFile.path))
                d->m_snapshot.remove(fileName);
            d->m_snapshot.remove(cxxFile.path);
623 624 625 626
        }
    }
}

627
QList<CppEditorDocumentHandle *> CppModelManager::cppEditorDocuments() const
628
{
629 630
    QMutexLocker locker(&d->m_cppEditorDocumentsMutex);
    return d->m_cppEditorDocuments.values();
631 632
}

633 634 635
/// \brief Remove all given files from the snapshot.
void CppModelManager::removeFilesFromSnapshot(const QSet<QString> &filesToRemove)
{
636
    QMutexLocker snapshotLocker(&d->m_snapshotMutex);
637 638
    QSetIterator<QString> i(filesToRemove);
    while (i.hasNext())
639
        d->m_snapshot.remove(i.next());
640 641 642
}

class ProjectInfoComparer
643
{
644
public:
645 646
    ProjectInfoComparer(const ProjectInfo &oldProjectInfo,
                        const ProjectInfo &newProjectInfo)
647
        : m_old(oldProjectInfo)
648
        , m_oldSourceFiles(oldProjectInfo.sourceFiles())
649
        , m_new(newProjectInfo)
650
        , m_newSourceFiles(newProjectInfo.sourceFiles())
651 652
    {}

653 654 655
    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); }
656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684

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

685
        return CppModelManager::timeStampModifiedFiles(documentsToCheck);
686 687 688
    }

private:
689
    const ProjectInfo &m_old;
690 691
    const QSet<QString> m_oldSourceFiles;

692
    const ProjectInfo &m_new;
693 694 695
    const QSet<QString> m_newSourceFiles;
};

696 697 698
/// Make sure that m_projectMutex is locked when calling this.
void CppModelManager::recalculateFileToProjectParts()
{
699 700 701
    d->m_projectFileToProjectPart.clear();
    d->m_fileToProjectParts.clear();
    foreach (const ProjectInfo &projectInfo, d->m_projectToProjectsInfo) {
702
        foreach (const ProjectPart::Ptr &projectPart, projectInfo.projectParts()) {
703
            d->m_projectFileToProjectPart[projectPart->projectFile] = projectPart;
704
            foreach (const ProjectFile &cxxFile, projectPart->files)
705 706
                d->m_fileToProjectParts[Utils::FileName::fromString(cxxFile.path)].append(
                            projectPart);
707

708 709 710 711
        }
    }
}

712
void CppModelManager::updateCppEditorDocuments() const
713
{
714 715
    // Refresh visible documents
    QSet<Core::IDocument *> visibleCppEditorDocuments;
716
    foreach (Core::IEditor *editor, Core::EditorManager::visibleEditors()) {
717
        if (Core::IDocument *document = editor->document()) {
718 719
            const QString filePath = document->filePath().toString();
            if (CppEditorDocumentHandle *theCppEditorDocument = cppEditorDocument(filePath)) {
720
                visibleCppEditorDocuments.insert(document);
721
                theCppEditorDocument->processor()->run();
722
            }
723 724 725
        }
    }

726 727 728 729 730
    // Mark invisible documents dirty
    QSet<Core::IDocument *> invisibleCppEditorDocuments
        = Core::DocumentModel::openedDocuments().toSet();
    invisibleCppEditorDocuments.subtract(visibleCppEditorDocuments);
    foreach (Core::IDocument *document, invisibleCppEditorDocuments) {
731 732 733
        const QString filePath = document->filePath().toString();
        if (CppEditorDocumentHandle *theCppEditorDocument = cppEditorDocument(filePath))
            theCppEditorDocument->setNeedsRefresh(true);
734 735 736
    }
}

737 738 739
QFuture<void> CppModelManager::updateProjectInfo(const ProjectInfo &newProjectInfo)
{
    if (!newProjectInfo.isValid())
740
        return QFuture<void>();
741

742
    QSet<QString> filesToReindex;
743 744
    bool filesRemoved = false;

745
    { // Only hold the mutex for a limited scope, so the dumping afterwards does not deadlock.
746
        QMutexLocker projectLocker(&d->m_projectMutex);
747 748

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

751
        // Check if we can avoid a full reindexing
752
        ProjectInfo oldProjectInfo = d->m_projectToProjectsInfo.value(project);
753
        if (oldProjectInfo.isValid()) {
754
            ProjectInfoComparer comparer(oldProjectInfo, newProjectInfo);
755

756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776
            if (comparer.configurationOrFilesChanged()) {
                d->m_dirty = true;

                // If the project configuration changed, do a full reindexing
                if (comparer.configurationChanged()) {
                    removeProjectInfoFilesAndIncludesFromSnapshot(oldProjectInfo);
                    filesToReindex.unite(newSourceFiles);

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

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

                    const QSet<QString> modifiedFiles = comparer.timeStampModifiedFiles(snapshot());
                    filesToReindex.unite(modifiedFiles);
777
                }
778

779 780 781 782 783 784 785
                // 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);
                }
786 787 788 789
            }

        // A new project was opened/created, do a full indexing
        } else {
790
            d->m_dirty = true;
791
            filesToReindex.unite(newSourceFiles);
792 793
        }

794
        // Update Project/ProjectInfo and File/ProjectPart table
795
        d->m_projectToProjectsInfo.insert(project, newProjectInfo);
796
        recalculateFileToProjectParts();
797

798 799 800
    } // Mutex scope

    // If requested, dump everything we got
801
    if (DumpProjectInfo)
802
        dumpModelManagerConfiguration(QLatin1String("updateProjectInfo"));
803

804 805 806 807 808
    // Remove files from snapshot that are not reachable any more
    if (filesRemoved)
        GC();

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

810 811 812 813
    // 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.
814
    updateCppEditorDocuments();
815

816 817
    // Trigger reindexing
    return updateSourceFiles(filesToReindex, ForcedProgressNotification);
818 819
}

820 821
ProjectPart::Ptr CppModelManager::projectPartForProjectFile(const QString &projectFile) const
{
822
    return d->m_projectFileToProjectPart.value(projectFile);
823 824
}

825
QList<ProjectPart::Ptr> CppModelManager::projectPart(const Utils::FileName &fileName) const
826
{
827 828
    QMutexLocker locker(&d->m_projectMutex);
    return d->m_fileToProjectParts.value(fileName);
829
}
830

831 832
QList<ProjectPart::Ptr> CppModelManager::projectPartFromDependencies(
        const Utils::FileName &fileName) const
833 834
{
    QSet<ProjectPart::Ptr> parts;
835
    const Utils::FileNameList deps = snapshot().filesDependingOn(fileName);
836

837 838
    QMutexLocker locker(&d->m_projectMutex);
    foreach (const Utils::FileName &dep, deps) {
839
        parts.unite(QSet<ProjectPart::Ptr>::fromList(d->m_fileToProjectParts.value(dep)));
840
    }
841

842
    return parts.values();
843
}
con's avatar
con committed
844

845 846 847 848
ProjectPart::Ptr CppModelManager::fallbackProjectPart() const
{
    ProjectPart::Ptr part(new ProjectPart);

849 850
    part->projectDefines = d->m_definedMacros;
    part->headerPaths = d->m_headerPaths;
851 852
    part->languageVersion = ProjectPart::CXX14;
    part->languageExtensions = ProjectPart::AllExtensions;
853
    part->qtVersion = ProjectPart::Qt5;
854
    part->updateLanguageFeatures();
855 856 857 858

    return part;
}

con's avatar
con committed
859 860
bool CppModelManager::isCppEditor(Core::IEditor *editor) const
{
861
    return editor->context().contains(ProjectExplorer::Constants::LANG_CXX);
con's avatar
con committed
862 863 864 865
}

void CppModelManager::emitDocumentUpdated(Document::Ptr doc)
{
866
    if (replaceDocument(doc))
867
        emit documentUpdated(doc);
Roberto Raggi's avatar
Roberto Raggi committed
868 869
}

870 871
void CppModelManager::onProjectAdded(ProjectExplorer::Project *)
{
872 873
    QMutexLocker locker(&d->m_projectMutex);
    d->m_dirty = true;
874 875
}