cppmodelmanager.cpp 33.4 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
namespace CppTools {
64

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

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

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

    return h;
}
78

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

101
} // namespace CppTools
102

103 104
using namespace CppTools;
using namespace CppTools::Internal;
con's avatar
con committed
105 106
using namespace CPlusPlus;

107
#ifdef QTCREATOR_WITH_DUMP_AST
108 109 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

#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
147
#endif // QTCREATOR_WITH_DUMP_AST
148

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

160 161 162 163
    "#define __complex__\n"
    "#define __imag__\n"
    "#define __real__\n"

164 165
    "#define __builtin_va_arg(a,b) ((b)0)\n"

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

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

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

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

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

196 197 198 199 200 201 202 203 204 205 206 207
    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);
208 209
}

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

216 217 218 219
    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
220 221
*/

222 223
QMutex CppModelManager::m_instanceMutex;
CppModelManager *CppModelManager::m_instance = 0;
224 225 226

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

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

    return m_instance;
235 236
}

237
CppModelManager::CppModelManager(QObject *parent)
238
    : CppModelManagerInterface(parent)
239
    , m_indexingSupporter(0)
240
    , m_enableGC(true)
con's avatar
con committed
241
{
Erik Verbruggen's avatar
Erik Verbruggen committed
242 243 244 245 246
    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
247
    m_findReferences = new CppFindReferences(this);
248
    m_indexerEnabled = qgetenv("QTCREATOR_NO_CODE_INDEXER").isNull();
Roberto Raggi's avatar
Roberto Raggi committed
249

250 251
    m_dirty = true;

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

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

266 267 268
    connect(Core::ICore::instance(), SIGNAL(coreAboutToClose()),
            this, SLOT(onCoreAboutToClose()));

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

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

276
    m_internalIndexingSupport = new BuiltinIndexingSupport;
con's avatar
con committed
277 278 279
}

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

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

290 291
Document::Ptr CppModelManager::document(const QString &fileName) const
{
292
    QMutexLocker locker(&m_snapshotMutex);
293 294 295 296 297 298 299 300
    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)
{
301
    QMutexLocker locker(&m_snapshotMutex);
302 303 304 305 306 307 308 309 310 311

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

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

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

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

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

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

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

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

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

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

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

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

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

480
    QMutexLocker locker(&m_cppEditorSupportsMutex);
481

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

490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514
/// \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;
515
        delayedGC();
516 517 518
    }
}

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

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

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

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

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

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

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

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

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

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

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

    return workingCopy;
}

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

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

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

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

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

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

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

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

719 720 721 722
        }
    }
}

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

728 729 730
    QStringList filesToReindex;
    bool filesRemoved = false;

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

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

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

744 745 746 747 748 749 750 751 752
            // 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());
753
                }
754 755 756 757 758 759 760 761

            // 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();
762
            }
763 764 765 766 767 768 769 770 771 772 773 774

            // 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;
775 776
        }

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

782 783 784
    } // Mutex scope

    // If requested, dump everything we got
785 786
    if (!qgetenv("QTCREATOR_DUMP_PROJECT_INFO").isEmpty())
        dumpModelManagerConfiguration();
787

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

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

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

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

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

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

818
    return parts.values();
819
}
con's avatar
con committed
820

821 822 823 824
ProjectPart::Ptr CppModelManager::fallbackProjectPart() const
{
    ProjectPart::Ptr part(new ProjectPart);

825
    part->projectDefines = m_definedMacros;
826 827 828 829 830 831 832 833 834 835
    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
836 837
bool CppModelManager::isCppEditor(Core::IEditor *editor) const
{
838
    return editor->context().contains(ProjectExplorer::Constants::LANG_CXX);
con's avatar
con committed
839 840 841 842
}

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

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

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

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

868 869 870 871 872 873 874
    delayedGC();
}

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

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

889 890 891 892 893
void CppModelManager::onCoreAboutToClose()
{
    m_enableGC = false;
}

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

899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915
    // 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();
916
    QSet<QString> reachableFiles;
917 918 919 920
    // 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
921

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

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

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

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

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

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

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

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

966 967
ModelManagerSupport *CppModelManager::modelManagerSupportForMimeType(const QString &mimeType) const
{
968 969 970
    QSharedPointer<CppCodeModelSettings> cms = CppToolsPlugin::instance()->codeModelSettings();
    const QString &id = cms->modelManagerSupportId(mimeType);
    return m_idTocodeModelSupporter.value(id, m_modelManagerSupportFallback.data());
971 972
}