cppmodelmanager.cpp 33.5 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 13 14
** 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
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
15
**
16
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17 18 19 20 21 22 23 24 25
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** 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
26 27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
hjk's avatar
hjk committed
29

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

32
#include "abstracteditorsupport.h"
33
#include "builtinindexingsupport.h"
34
#include "cppcodemodelsettings.h"
35
#include "cppfindreferences.h"
36
#include "cpphighlightingsupport.h"
37
#include "cppindexingsupport.h"
38
#include "cppmodelmanagersupportinternal.h"
39 40
#include "cpppreprocessor.h"
#include "cpptoolsconstants.h"
41
#include "cpptoolseditorsupport.h"
42
#include "cpptoolsplugin.h"
con's avatar
con committed
43

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

51 52 53 54
#include <QCoreApplication>
#include <QDebug>
#include <QMutexLocker>
#include <QTextBlock>
55
#include <QTimer>
56

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

63 64
static const bool DumpProjectInfo = qgetenv("QTC_DUMP_PROJECT_INFO") == "1";

65
namespace CppTools {
66

67
uint qHash(const ProjectPart &p)
68
{
69
    uint h = qHash(p.toolchainDefines) ^ qHash(p.projectDefines) ^ p.cVersion ^ p.cxxVersion
70
            ^ p.cxxExtensions ^ p.qtVersion ^ qHash(p.projectConfigFile);
71 72 73 74 75 76 77 78 79

    foreach (const QString &i, p.includePaths)
        h ^= qHash(i);

    foreach (const QString &f, p.frameworkPaths)
        h ^= qHash(f);

    return h;
}
80

81 82
bool operator==(const ProjectPart &p1,
                const ProjectPart &p2)
83
{
84 85 86
    if (p1.toolchainDefines != p2.toolchainDefines)
        return false;
    if (p1.projectDefines != p2.projectDefines)
87
        return false;
88 89
    if (p1.projectConfigFile != p2.projectConfigFile)
        return false;
90 91 92 93 94
    if (p1.cVersion != p2.cVersion)
        return false;
    if (p1.cxxVersion != p2.cxxVersion)
        return false;
    if (p1.cxxExtensions != p2.cxxExtensions)
95
        return false;
96
    if (p1.qtVersion!= p2.qtVersion)
97 98 99 100 101
        return false;
    if (p1.includePaths != p2.includePaths)
        return false;
    return p1.frameworkPaths == p2.frameworkPaths;
}
102

103
} // namespace CppTools
104

105 106
using namespace CppTools;
using namespace CppTools::Internal;
con's avatar
con committed
107 108
using namespace CPlusPlus;

109
#ifdef QTCREATOR_WITH_DUMP_AST
110 111 112 113 114 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

#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
149
#endif // QTCREATOR_WITH_DUMP_AST
150

con's avatar
con committed
151 152
static const char pp_configuration[] =
    "# 1 \"<configuration>\"\n"
153
    "#define Q_CREATOR_RUN 1\n"
con's avatar
con committed
154 155 156 157 158 159
    "#define __cplusplus 1\n"
    "#define __extension__\n"
    "#define __context__\n"
    "#define __range__\n"
    "#define   restrict\n"
    "#define __restrict\n"
160
    "#define __restrict__\n"
con's avatar
con committed
161

162 163 164 165
    "#define __complex__\n"
    "#define __imag__\n"
    "#define __real__\n"

166 167
    "#define __builtin_va_arg(a,b) ((b)0)\n"

con's avatar
con committed
168 169
    // ### add macros for win32
    "#define __cdecl\n"
170
    "#define __stdcall\n"
con's avatar
con committed
171 172 173 174 175
    "#define QT_WA(x) x\n"
    "#define CALLBACK\n"
    "#define STDMETHODCALLTYPE\n"
    "#define __RPC_FAR\n"
    "#define __declspec(a)\n"
176 177 178
    "#define STDMETHOD(method) virtual HRESULT STDMETHODCALLTYPE method\n"
    "#define __try try\n"
    "#define __except catch\n"
179 180 181
    "#define __finally\n"
    "#define __inline inline\n"
    "#define __forceinline inline\n";
con's avatar
con committed
182

183
QStringList CppModelManager::timeStampModifiedFiles(const QList<Document::Ptr> documentsToCheck)
184 185 186
{
    QStringList sourceFiles;

187
    foreach (const Document::Ptr doc, documentsToCheck) {
188 189
        const QDateTime lastModified = doc->lastModified();

190
        if (!lastModified.isNull()) {
191 192 193 194 195 196 197
            QFileInfo fileInfo(doc->fileName());

            if (fileInfo.exists() && fileInfo.lastModified() != lastModified)
                sourceFiles.append(doc->fileName());
        }
    }

198 199 200 201 202 203 204 205 206 207 208 209
    return sourceFiles;
}

void CppModelManager::updateModifiedSourceFiles()
{
    const Snapshot snapshot = this->snapshot();
    QList<Document::Ptr> documentsToCheck;
    foreach (const Document::Ptr document, snapshot)
        documentsToCheck << document;

    const QStringList filesToUpdate = timeStampModifiedFiles(documentsToCheck);
    updateSourceFiles(filesToUpdate);
210 211
}

con's avatar
con committed
212 213
/*!
    \class CppTools::CppModelManager
214 215 216
    \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
217

218 219 220 221
    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
222 223
*/

224 225
QMutex CppModelManager::m_instanceMutex;
CppModelManager *CppModelManager::m_instance = 0;
226 227 228

CppModelManager *CppModelManager::instance()
{
229 230 231 232 233 234 235 236
    if (m_instance)
        return m_instance;

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

    return m_instance;
237 238
}

239
CppModelManager::CppModelManager(QObject *parent)
240
    : CppModelManagerInterface(parent)
241
    , m_indexingSupporter(0)
242
    , m_enableGC(true)
con's avatar
con committed
243
{
Erik Verbruggen's avatar
Erik Verbruggen committed
244 245 246 247 248
    connect(this, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)),
            this, SIGNAL(globalSnapshotChanged()));
    connect(this, SIGNAL(aboutToRemoveFiles(QStringList)),
            this, SIGNAL(globalSnapshotChanged()));

Roberto Raggi's avatar
Roberto Raggi committed
249
    m_findReferences = new CppFindReferences(this);
250
    m_indexerEnabled = qgetenv("QTC_NO_CODE_INDEXER") != "1";
Roberto Raggi's avatar
Roberto Raggi committed
251

252 253
    m_dirty = true;

254 255 256 257
    m_delayedGcTimer = new QTimer(this);
    m_delayedGcTimer->setSingleShot(true);
    connect(m_delayedGcTimer, SIGNAL(timeout()), this, SLOT(GC()));

hjk's avatar
hjk committed
258 259
    QObject *sessionManager = ProjectExplorer::SessionManager::instance();
    connect(sessionManager, SIGNAL(projectAdded(ProjectExplorer::Project*)),
260
            this, SLOT(onProjectAdded(ProjectExplorer::Project*)));
hjk's avatar
hjk committed
261
    connect(sessionManager, SIGNAL(aboutToRemoveProject(ProjectExplorer::Project*)),
Robert Loehning's avatar
Robert Loehning committed
262
            this, SLOT(onAboutToRemoveProject(ProjectExplorer::Project*)));
hjk's avatar
hjk committed
263
    connect(sessionManager, SIGNAL(aboutToLoadSession(QString)),
264
            this, SLOT(onAboutToLoadSession()));
hjk's avatar
hjk committed
265
    connect(sessionManager, SIGNAL(aboutToUnloadSession(QString)),
266
            this, SLOT(onAboutToUnloadSession()));
con's avatar
con committed
267

268 269 270
    connect(Core::ICore::instance(), SIGNAL(coreAboutToClose()),
            this, SLOT(onCoreAboutToClose()));

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

273
    m_modelManagerSupportFallback.reset(new ModelManagerSupportInternal);
274 275
    CppToolsPlugin::instance()->codeModelSettings()->setDefaultId(
                m_modelManagerSupportFallback->id());
276 277
    addModelManagerSupport(m_modelManagerSupportFallback.data());

278
    m_internalIndexingSupport = new BuiltinIndexingSupport;
con's avatar
con committed
279 280 281
}

CppModelManager::~CppModelManager()
282
{
283
    delete m_internalIndexingSupport;
284
}
con's avatar
con committed
285

286
Snapshot CppModelManager::snapshot() const
287
{
288
    QMutexLocker locker(&m_snapshotMutex);
289 290
    return m_snapshot;
}
con's avatar
con committed
291

292 293
Document::Ptr CppModelManager::document(const QString &fileName) const
{
294
    QMutexLocker locker(&m_snapshotMutex);
295 296 297 298 299 300 301 302
    return m_snapshot.document(fileName);
}

/// Replace the document in the snapshot.
///
/// \returns true if successful, false if the new document is out-dated.
bool CppModelManager::replaceDocument(Document::Ptr newDoc)
{
303
    QMutexLocker locker(&m_snapshotMutex);
304 305 306 307 308 309 310 311 312 313

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

    m_snapshot.insert(newDoc);
    return true;
}

314 315
void CppModelManager::ensureUpdated()
{
316
    QMutexLocker locker(&m_projectMutex);
317
    if (!m_dirty)
318 319
        return;

320 321 322 323
    m_projectFiles = internalProjectFiles();
    m_includePaths = internalIncludePaths();
    m_frameworkPaths = internalFrameworkPaths();
    m_definedMacros = internalDefinedMacros();
324 325 326
    m_dirty = false;
}

327
QStringList CppModelManager::internalProjectFiles() const
con's avatar
con committed
328 329
{
    QStringList files;
330
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projectToProjectsInfo);
con's avatar
con committed
331 332
    while (it.hasNext()) {
        it.next();
333
        const ProjectInfo pinfo = it.value();
334
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
335 336
            foreach (const ProjectFile &file, part->files)
                files += file.path;
337
        }
con's avatar
con committed
338
    }
339
    files.removeDuplicates();
con's avatar
con committed
340 341 342
    return files;
}

343
QStringList CppModelManager::internalIncludePaths() const
con's avatar
con committed
344 345
{
    QStringList includePaths;
346
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projectToProjectsInfo);
con's avatar
con committed
347 348
    while (it.hasNext()) {
        it.next();
349
        const ProjectInfo pinfo = it.value();
350
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts())
351 352
            foreach (const QString &path, part->includePaths)
                includePaths.append(CppPreprocessor::cleanPath(path));
con's avatar
con committed
353
    }
354
    includePaths.removeDuplicates();
con's avatar
con committed
355 356 357
    return includePaths;
}

358
QStringList CppModelManager::internalFrameworkPaths() const
con's avatar
con committed
359 360
{
    QStringList frameworkPaths;
361
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projectToProjectsInfo);
con's avatar
con committed
362 363
    while (it.hasNext()) {
        it.next();
364
        const ProjectInfo pinfo = it.value();
365
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts())
366 367
            foreach (const QString &path, part->frameworkPaths)
                frameworkPaths.append(CppPreprocessor::cleanPath(path));
con's avatar
con committed
368
    }
369
    frameworkPaths.removeDuplicates();
con's avatar
con committed
370 371 372
    return frameworkPaths;
}

373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388
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);
        }
    }
}

389
QByteArray CppModelManager::internalDefinedMacros() const
con's avatar
con committed
390 391
{
    QByteArray macros;
392
    QSet<QByteArray> alreadyIn;
393
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projectToProjectsInfo);
con's avatar
con committed
394 395
    while (it.hasNext()) {
        it.next();
396
        const ProjectInfo pinfo = it.value();
397
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
398 399
            addUnique(part->toolchainDefines.split('\n'), &macros, &alreadyIn);
            addUnique(part->projectDefines.split('\n'), &macros, &alreadyIn);
400 401
            if (!part->projectConfigFile.isEmpty())
                macros += readProjectConfigFile(part);
402
        }
con's avatar
con committed
403 404 405 406
    }
    return macros;
}

Sergio Ahumada's avatar
Sergio Ahumada committed
407
/// This function will acquire the mutex!
408 409 410
void CppModelManager::dumpModelManagerConfiguration()
{
    // Tons of debug output...
411 412
    qDebug() << "========= CppModelManager::dumpModelManagerConfiguration ======";
    foreach (const ProjectInfo &pinfo, m_projectToProjectsInfo) {
413
        qDebug() << " for project:"<< pinfo.project().data()->projectFilePath();
414 415
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
            qDebug() << "=== part ===";
416 417 418 419 420 421 422
            const char* cVersion;
            switch (part->cVersion) {
            case ProjectPart::C89: cVersion = "C89"; break;
            case ProjectPart::C99: cVersion = "C99"; break;
            case ProjectPart::C11: cVersion = "C11"; break;
            default: cVersion = "INVALID";
            }
423
            const char* cxxVersion;
424
            switch (part->cxxVersion) {
425 426
            case ProjectPart::CXX98: cxxVersion = "CXX98"; break;
            case ProjectPart::CXX11: cxxVersion = "CXX11"; break;
427 428
            default: cxxVersion = "INVALID";
            }
429 430 431 432 433 434 435
            QStringList cxxExtensions;
            if (part->cxxExtensions & ProjectPart::GnuExtensions)
                cxxExtensions << QLatin1String("GnuExtensions");
            if (part->cxxExtensions & ProjectPart::MicrosoftExtensions)
                cxxExtensions << QLatin1String("MicrosoftExtensions");
            if (part->cxxExtensions & ProjectPart::BorlandExtensions)
                cxxExtensions << QLatin1String("BorlandExtensions");
436
            if (part->cxxExtensions & ProjectPart::OpenMPExtensions)
437
                cxxExtensions << QLatin1String("OpenMP");
438

439 440 441
            qDebug() << "cVersion:" << cVersion;
            qDebug() << "cxxVersion:" << cxxVersion;
            qDebug() << "cxxExtensions:" << cxxExtensions;
442
            qDebug() << "Qt version:" << part->qtVersion;
443
            qDebug() << "project config file:" << part->projectConfigFile;
444
            qDebug() << "precompiled header:" << part->precompiledHeaders;
445 446
            qDebug() << "toolchain defines:" << part->toolchainDefines;
            qDebug() << "project defines:" << part->projectDefines;
447 448
            qDebug() << "includes:" << part->includePaths;
            qDebug() << "frameworkPaths:" << part->frameworkPaths;
449
            qDebug() << "files:" << part->files;
450 451 452 453 454 455 456 457 458 459 460 461 462
            qDebug() << "";
        }
    }

    ensureUpdated();
    qDebug() << "=== Merged include paths ===";
    foreach (const QString &inc, m_includePaths)
        qDebug() << inc;
    qDebug() << "=== Merged framework paths ===";
    foreach (const QString &inc, m_frameworkPaths)
        qDebug() << inc;
    qDebug() << "=== Merged defined macros ===";
    qDebug() << m_definedMacros;
463
    qDebug() << "========= End of dump ======";
464 465
}

466
void CppModelManager::addExtraEditorSupport(AbstractEditorSupport *editorSupport)
467
{
468
    m_extraEditorSupports.insert(editorSupport);
469 470
}

471
void CppModelManager::removeExtraEditorSupport(AbstractEditorSupport *editorSupport)
472
{
473
    m_extraEditorSupports.remove(editorSupport);
474 475
}

476 477
/// \brief Returns the \c CppEditorSupport for the given text editor. It will
///        create one when none exists yet.
478
CppEditorSupport *CppModelManager::cppEditorSupport(TextEditor::BaseTextEditor *textEditor)
479
{
480
    Q_ASSERT(textEditor);
481

482
    QMutexLocker locker(&m_cppEditorSupportsMutex);
483

484
    CppEditorSupport *editorSupport = m_cppEditorSupports.value(textEditor, 0);
485
    if (!editorSupport && isCppEditor(textEditor)) {
486 487
        editorSupport = new CppEditorSupport(this, textEditor);
        m_cppEditorSupports.insert(textEditor, editorSupport);
488 489 490 491
    }
    return editorSupport;
}

492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516
/// \brief Removes the CppEditorSupport for the closed editor.
void CppModelManager::deleteCppEditorSupport(TextEditor::BaseTextEditor *textEditor)
{
    static short numberOfClosedEditors = 0;

    QTC_ASSERT(textEditor, return);

    if (!isCppEditor(textEditor))
        return;

    CppEditorSupport *editorSupport;
    int numberOfOpenEditors = 0;

    { // Only lock the operations on m_cppEditorSupport
        QMutexLocker locker(&m_cppEditorSupportsMutex);
        editorSupport = m_cppEditorSupports.value(textEditor, 0);
        m_cppEditorSupports.remove(textEditor);
        numberOfOpenEditors = m_cppEditorSupports.size();
    }

    delete editorSupport;

    ++numberOfClosedEditors;
    if (numberOfOpenEditors == 0 || numberOfClosedEditors == 5) {
        numberOfClosedEditors = 0;
517
        delayedGC();
518 519 520
    }
}

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

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

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

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

545 546 547 548 549
void CppModelManager::renameMacroUsages(const CPlusPlus::Macro &macro, const QString &replacement)
{
    m_findReferences->renameMacroUses(macro, replacement);
}

550 551
void CppModelManager::replaceSnapshot(const CPlusPlus::Snapshot &newSnapshot)
{
552
    QMutexLocker snapshotLocker(&m_snapshotMutex);
553 554 555
    m_snapshot = newSnapshot;
}

556
CppModelManager::WorkingCopy CppModelManager::buildWorkingCopyList()
con's avatar
con committed
557
{
558
    QList<CppEditorSupport *> cppEditorSupports;
559 560

    {
561 562
        QMutexLocker locker(&m_cppEditorSupportsMutex);
        cppEditorSupports = m_cppEditorSupports.values();
563 564
    }

565
    WorkingCopy workingCopy;
566
    foreach (const CppEditorSupport *editorSupport, cppEditorSupports) {
567 568
        workingCopy.insert(editorSupport->fileName(), editorSupport->contents(),
                           editorSupport->editorRevision());
con's avatar
con committed
569 570
    }

571 572 573
    QSetIterator<AbstractEditorSupport *> it(m_extraEditorSupports);
    while (it.hasNext()) {
        AbstractEditorSupport *es = it.next();
574
        workingCopy.insert(es->fileName(), es->contents(), es->revision());
575 576
    }

577
    // Add the project configuration file
578
    QByteArray conf = codeModelConfiguration();
con's avatar
con committed
579
    conf += definedMacros();
580
    workingCopy.insert(configurationFileName(), conf);
con's avatar
con committed
581 582 583 584

    return workingCopy;
}

585
CppModelManager::WorkingCopy CppModelManager::workingCopy() const
586 587 588 589
{
    return const_cast<CppModelManager *>(this)->buildWorkingCopyList();
}

590 591 592 593 594
QByteArray CppModelManager::codeModelConfiguration() const
{
    return QByteArray::fromRawData(pp_configuration, qstrlen(pp_configuration));
}

595 596
QFuture<void> CppModelManager::updateSourceFiles(const QStringList &sourceFiles,
                                                 ProgressNotificationMode mode)
597 598 599 600
{
    if (sourceFiles.isEmpty() || !m_indexerEnabled)
        return QFuture<void>();

601
    if (m_indexingSupporter)
602 603
        m_indexingSupporter->refreshSourceFiles(sourceFiles, mode);
    return m_internalIndexingSupport->refreshSourceFiles(sourceFiles, mode);
604
}
con's avatar
con committed
605

606 607
QList<CppModelManager::ProjectInfo> CppModelManager::projectInfos() const
{
608
    QMutexLocker locker(&m_projectMutex);
609
    return m_projectToProjectsInfo.values();
610 611 612 613
}

CppModelManager::ProjectInfo CppModelManager::projectInfo(ProjectExplorer::Project *project) const
{
614
    QMutexLocker locker(&m_projectMutex);
615
    return m_projectToProjectsInfo.value(project, ProjectInfo(project));
616 617
}

618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643
/// \brief Remove all files and their includes (recursively) of given ProjectInfo from the snapshot.
void CppModelManager::removeProjectInfoFilesAndIncludesFromSnapshot(const ProjectInfo &projectInfo)
{
    if (!projectInfo.isValid())
        return;

    QMutexLocker snapshotLocker(&m_snapshotMutex);
    foreach (const ProjectPart::Ptr &projectPart, projectInfo.projectParts()) {
        foreach (const ProjectFile &cxxFile, projectPart->files) {
            foreach (const QString &fileName, m_snapshot.allIncludesForDocument(cxxFile.path))
                m_snapshot.remove(fileName);
            m_snapshot.remove(cxxFile.path);
        }
    }
}

/// \brief Remove all given files from the snapshot.
void CppModelManager::removeFilesFromSnapshot(const QSet<QString> &filesToRemove)
{
    QMutexLocker snapshotLocker(&m_snapshotMutex);
    QSetIterator<QString> i(filesToRemove);
    while (i.hasNext())
        m_snapshot.remove(i.next());
}

class ProjectInfoComparer
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 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709
public:
    ProjectInfoComparer(const CppModelManager::ProjectInfo &oldProjectInfo,
                        const CppModelManager::ProjectInfo &newProjectInfo)
        : m_old(oldProjectInfo)
        , m_oldSourceFiles(oldProjectInfo.sourceFiles().toSet())
        , m_new(newProjectInfo)
        , m_newSourceFiles(newProjectInfo.sourceFiles().toSet())
    {}

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

    bool configurationChanged() const
    {
        return definesChanged()
            || m_new.includePaths() != m_old.includePaths()
            || m_new.frameworkPaths() != m_old.frameworkPaths();
    }

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

        return CppModelManager::timeStampModifiedFiles(documentsToCheck).toSet();
    }

private:
    const CppModelManager::ProjectInfo &m_old;
    const QSet<QString> m_oldSourceFiles;

    const CppModelManager::ProjectInfo &m_new;
    const QSet<QString> m_newSourceFiles;
};

710 711 712
/// Make sure that m_projectMutex is locked when calling this.
void CppModelManager::recalculateFileToProjectParts()
{
713
    m_projectFileToProjectPart.clear();
714 715 716
    m_fileToProjectParts.clear();
    foreach (const ProjectInfo &projectInfo, m_projectToProjectsInfo) {
        foreach (const ProjectPart::Ptr &projectPart, projectInfo.projectParts()) {
717 718
            m_projectFileToProjectPart[projectPart->projectFile] = projectPart;
            foreach (const ProjectFile &cxxFile, projectPart->files)
719
                m_fileToProjectParts[cxxFile.path].append(projectPart);
720

721 722 723 724
        }
    }
}

725 726 727
QFuture<void> CppModelManager::updateProjectInfo(const ProjectInfo &newProjectInfo)
{
    if (!newProjectInfo.isValid())
728
        return QFuture<void>();
729

730 731 732
    QStringList filesToReindex;
    bool filesRemoved = false;

733
    { // Only hold the mutex for a limited scope, so the dumping afterwards does not deadlock.
734 735 736 737
        QMutexLocker projectLocker(&m_projectMutex);

        ProjectExplorer::Project *project = newProjectInfo.project().data();
        const QStringList newSourceFiles = newProjectInfo.sourceFiles();
738

739
        // Check if we can avoid a full reindexing
740
        ProjectInfo oldProjectInfo = m_projectToProjectsInfo.value(project);
741
        if (oldProjectInfo.isValid()) {
742 743
            ProjectInfoComparer comparer(oldProjectInfo, newProjectInfo);
            if (comparer.nothingChanged())
744 745
                return QFuture<void>();

746 747 748 749 750 751 752 753 754
            // If the project configuration changed, do a full reindexing
            if (comparer.configurationChanged()) {
                removeProjectInfoFilesAndIncludesFromSnapshot(oldProjectInfo);
                filesToReindex << newSourceFiles;

                // The "configuration file" includes all defines and therefore should be updated
                if (comparer.definesChanged()) {
                    QMutexLocker snapshotLocker(&m_snapshotMutex);
                    m_snapshot.remove(configurationFileName());
755
                }
756 757 758 759 760 761 762 763

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

                const QSet<QString> modifiedFiles = comparer.timeStampModifiedFiles(snapshot());
                filesToReindex << modifiedFiles.toList();
764
            }
765 766 767 768 769 770 771 772 773 774 775 776

            // 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 {
            filesToReindex << newSourceFiles;
777 778
        }

779
        // Update Project/ProjectInfo and File/ProjectPart table
780
        m_dirty = true;
781
        m_projectToProjectsInfo.insert(project, newProjectInfo);
782
        recalculateFileToProjectParts();
783

784 785 786
    } // Mutex scope

    // If requested, dump everything we got
787
    if (DumpProjectInfo)
788
        dumpModelManagerConfiguration();
789

790 791 792 793 794
    // Remove files from snapshot that are not reachable any more
    if (filesRemoved)
        GC();

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

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

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

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

811 812 813
QList<ProjectPart::Ptr> CppModelManager::projectPartFromDependencies(const QString &fileName) const
{
    QSet<ProjectPart::Ptr> parts;
814 815
    DependencyTable table;
    table.build(snapshot());
816
    const QStringList deps = table.filesDependingOn(fileName);
817 818
    foreach (const QString &dep, deps)
        parts.unite(QSet<ProjectPart::Ptr>::fromList(m_fileToProjectParts.value(dep)));
819

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

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

827
    part->projectDefines = m_definedMacros;
828 829 830 831 832 833 834 835 836 837
    part->includePaths = m_includePaths;
    part->frameworkPaths = m_frameworkPaths;
    part->cVersion = ProjectPart::C11;
    part->cxxVersion = ProjectPart::CXX11;
    part->cxxExtensions = ProjectPart::AllExtensions;
    part->qtVersion = ProjectPart::Qt5;

    return part;
}

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

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

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

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

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

870 871 872 873 874 875 876
    delayedGC();
}

void CppModelManager::onAboutToLoadSession()
{
    if (m_delayedGcTimer->isActive())
        m_delayedGcTimer->stop();
con's avatar
con committed
877 878 879
    GC();
}

880
void CppModelManager::onAboutToUnloadSession()
con's avatar
con committed
881
{
882
    Core::ProgressManager::cancelTasks(CppTools::Constants::TASK_INDEX);
883
    do {
884
        QMutexLocker locker(&m_projectMutex);
885
        m_projectToProjectsInfo.clear();
886
        recalculateFileToProjectParts();
887 888
        m_dirty = true;
    } while (0);
con's avatar
con committed
889 890
}

891 892 893 894 895
void CppModelManager::onCoreAboutToClose()
{
    m_enableGC = false;
}

con's avatar
con committed
896 897
void CppModelManager::GC()
{
898 899 900
    if (!m_enableGC)
        return;

901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917
    // Collect files of CppEditorSupport and AbstractEditorSupport.
    QStringList filesInEditorSupports;
    QList<CppEditorSupport *> cppEditorSupports;
    {
        QMutexLocker locker(&m_cppEditorSupportsMutex);
        cppEditorSupports = m_cppEditorSupports.values();
    }
    foreach (const CppEditorSupport *cppEditorSupport, cppEditorSupports)
        filesInEditorSupports << cppEditorSupport->fileName();

    QSetIterator<AbstractEditorSupport *> jt(m_extraEditorSupports);
    while (jt.hasNext()) {
        AbstractEditorSupport *abstractEditorSupport = jt.next();
        filesInEditorSupports << abstractEditorSupport->fileName();
    }

    Snapshot currentSnapshot = snapshot();
918
    QSet<QString> reachableFiles;
919 920 921 922
    // The configuration file is part of the project files, which is just fine.
    // If single files are open, without any project, then there is no need to
    // keep the configuration file around.
    QStringList todo = filesInEditorSupports + projectFiles();
con's avatar
con committed
923

924
    // Collect all files that are reachable from the project files
925
    while (!todo.isEmpty()) {
926
        const QString file = todo.last();
con's avatar
con committed
927 928
        todo.removeLast();

929
        if (reachableFiles.contains(file))
con's avatar
con committed
930
            continue;
931
        reachableFiles.insert(file);
con's avatar
con committed
932

933
        if (Document::Ptr doc = currentSnapshot.document(file))
con's avatar
con committed
934 935 936
            todo += doc->includedFiles();
    }

937 938
    // Find out the files in the current snapshot that are not reachable from the project files
    QStringList notReachableFiles;
939 940 941 942
    Snapshot newSnapshot;
    for (Snapshot::const_iterator it = currentSnapshot.begin(); it != currentSnapshot.end(); ++it) {
        const QString fileName = it.key();

943
        if (reachableFiles.contains(fileName))
944 945
            newSnapshot.insert(it.value());
        else
946
            notReachableFiles.append(fileName);
con's avatar
con committed
947 948
    }

949 950
    // Announce removing files and replace the snapshot
    emit aboutToRemoveFiles(notReachableFiles);
951
    replaceSnapshot(newSnapshot);
952
    emit gcFinished();
con's avatar
con committed
953
}
954

955 956 957 958 959
void CppModelManager::finishedRefreshingSourceFiles(const QStringList &files)
{
    emit sourceFilesRefreshed(files);
}

960
void CppModelManager::addModelManagerSupport(ModelManagerSupport *modelManagerSupport)
961
{
962 963 964 965
    Q_ASSERT(modelManagerSupport);
    m_idTocodeModelSupporter[modelManagerSupport->id()] = modelManagerSupport;
    QSharedPointer<CppCodeModelSettings> cms = CppToolsPlugin::instance()->codeModelSettings();
    cms->setModelManagerSupports(m_idTocodeModelSupporter.values());
966
}
967

968 969
ModelManagerSupport *CppModelManager::modelManagerSupportForMimeType(const QString &mimeType) const
{
970 971 972
    QSharedPointer<CppCodeModelSettings> cms = CppToolsPlugin::instance()->codeModelSettings();
    const