cppmodelmanager.cpp 36.4 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 "cpptoolsreuse.h"
46
#include "editordocumenthandle.h"
con's avatar
con committed
47

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

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

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

73 74
Q_DECLARE_METATYPE(QSet<QString>)

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

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

81
#ifdef QTCREATOR_WITH_DUMP_AST
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 120

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

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

static QMutex m_instanceMutex;
static CppModelManager *m_instance;

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

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

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

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

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

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

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

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

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

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

220 221 222
    return sourceFiles;
}

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

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

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

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

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

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

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

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

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

    return m_instance;
288 289
}

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

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

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

303
    d->m_dirty = true;
304

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

506
    {
507 508 509 510
        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();
511 512
    }

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

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

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

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

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

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

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

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

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

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

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

    return workingCopy;
}

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

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

589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609
static QSet<QString> tooBigFilesRemoved(const QSet<QString> &files, int fileSizeLimit)
{
    if (fileSizeLimit == 0)
        return files;

    QSet<QString> result;
    QFileInfo fileInfo;

    QSetIterator<QString> i(files);
    while (i.hasNext()) {
        const QString filePath = i.next();
        fileInfo.setFile(filePath);
        if (skipFileDueToSizeLimit(fileInfo), fileSizeLimit)
            continue;

        result << filePath;
    }

    return result;
}

610
QFuture<void> CppModelManager::updateSourceFiles(const QSet<QString> &sourceFiles,
611
                                                 ProgressNotificationMode mode)
612
{
613
    if (sourceFiles.isEmpty() || !d->m_indexerEnabled)
614 615
        return QFuture<void>();

616 617
    const auto filteredFiles = tooBigFilesRemoved(sourceFiles, fileSizeLimit());

618
    if (d->m_indexingSupporter)
619 620
        d->m_indexingSupporter->refreshSourceFiles(filteredFiles, mode);
    return d->m_internalIndexingSupport->refreshSourceFiles(filteredFiles, mode);
621
}
con's avatar
con committed
622

623
QList<ProjectInfo> CppModelManager::projectInfos() const
624
{
625 626
    QMutexLocker locker(&d->m_projectMutex);
    return d->m_projectToProjectsInfo.values();
627 628
}

629
ProjectInfo CppModelManager::projectInfo(ProjectExplorer::Project *project) const
630
{
631
    QMutexLocker locker(&d->m_projectMutex);
632
    return d->m_projectToProjectsInfo.value(project, ProjectInfo());
633 634
}

635 636 637 638 639 640
/// \brief Remove all files and their includes (recursively) of given ProjectInfo from the snapshot.
void CppModelManager::removeProjectInfoFilesAndIncludesFromSnapshot(const ProjectInfo &projectInfo)
{
    if (!projectInfo.isValid())
        return;

641
    QMutexLocker snapshotLocker(&d->m_snapshotMutex);
642 643
    foreach (const ProjectPart::Ptr &projectPart, projectInfo.projectParts()) {
        foreach (const ProjectFile &cxxFile, projectPart->files) {
644 645 646
            foreach (const QString &fileName, d->m_snapshot.allIncludesForDocument(cxxFile.path))
                d->m_snapshot.remove(fileName);
            d->m_snapshot.remove(cxxFile.path);
647 648 649 650
        }
    }
}

651
QList<CppEditorDocumentHandle *> CppModelManager::cppEditorDocuments() const
652
{
653 654
    QMutexLocker locker(&d->m_cppEditorDocumentsMutex);
    return d->m_cppEditorDocuments.values();
655 656
}

657 658 659
/// \brief Remove all given files from the snapshot.
void CppModelManager::removeFilesFromSnapshot(const QSet<QString> &filesToRemove)
{
660
    QMutexLocker snapshotLocker(&d->m_snapshotMutex);
661 662
    QSetIterator<QString> i(filesToRemove);
    while (i.hasNext())
663
        d->m_snapshot.remove(i.next());
664 665 666
}

class ProjectInfoComparer
667
{
668
public:
669 670
    ProjectInfoComparer(const ProjectInfo &oldProjectInfo,
                        const ProjectInfo &newProjectInfo)
671
        : m_old(oldProjectInfo)
672
        , m_oldSourceFiles(oldProjectInfo.sourceFiles())
673
        , m_new(newProjectInfo)
674
        , m_newSourceFiles(newProjectInfo.sourceFiles())
675 676
    {}

677 678 679
    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); }
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

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

709
        return CppModelManager::timeStampModifiedFiles(documentsToCheck);
710 711 712
    }

private:
713
    const ProjectInfo &m_old;
714 715
    const QSet<QString> m_oldSourceFiles;

716
    const ProjectInfo &m_new;
717 718 719
    const QSet<QString> m_newSourceFiles;
};

720 721 722
/// Make sure that m_projectMutex is locked when calling this.
void CppModelManager::recalculateFileToProjectParts()
{
723 724 725
    d->m_projectFileToProjectPart.clear();
    d->m_fileToProjectParts.clear();
    foreach (const ProjectInfo &projectInfo, d->m_projectToProjectsInfo) {
726
        foreach (const ProjectPart::Ptr &projectPart, projectInfo.projectParts()) {
727
            d->m_projectFileToProjectPart[projectPart->projectFile] = projectPart;
728
            foreach (const ProjectFile &cxxFile, projectPart->files)
729 730
                d->m_fileToProjectParts[Utils::FileName::fromString(cxxFile.path)].append(
                            projectPart);
731

732 733 734 735
        }
    }
}

736
void CppModelManager::updateCppEditorDocuments() const
737
{
738 739
    // Refresh visible documents
    QSet<Core::IDocument *> visibleCppEditorDocuments;
740
    foreach (Core::IEditor *editor, Core::EditorManager::visibleEditors()) {
741
        if (Core::IDocument *document = editor->document()) {
742 743
            const QString filePath = document->filePath().toString();
            if (CppEditorDocumentHandle *theCppEditorDocument = cppEditorDocument(filePath)) {
744
                visibleCppEditorDocuments.insert(document);
745
                theCppEditorDocument->processor()->run();
746
            }
747 748 749
        }
    }

750 751 752 753 754
    // Mark invisible documents dirty
    QSet<Core::IDocument *> invisibleCppEditorDocuments
        = Core::DocumentModel::openedDocuments().toSet();
    invisibleCppEditorDocuments.subtract(visibleCppEditorDocuments);
    foreach (Core::IDocument *document, invisibleCppEditorDocuments) {
755 756 757
        const QString filePath = document->filePath().toString();
        if (CppEditorDocumentHandle *theCppEditorDocument = cppEditorDocument(filePath))
            theCppEditorDocument->setNeedsRefresh(true);
758 759 760
    }
}

761 762 763
QFuture<void> CppModelManager::updateProjectInfo(const ProjectInfo &newProjectInfo)
{
    if (!newProjectInfo.isValid())
764
        return QFuture<void>();
765

766
    QSet<QString> filesToReindex;
767 768
    bool filesRemoved = false;

769
    { // Only hold the mutex for a limited scope, so the dumping afterwards does not deadlock.
770
        QMutexLocker projectLocker(&d->m_projectMutex);
771 772

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

775
        // Check if we can avoid a full reindexing
776
        ProjectInfo oldProjectInfo = d->m_projectToProjectsInfo.value(project);
777
        if (oldProjectInfo.isValid()) {
778
            ProjectInfoComparer comparer(oldProjectInfo, newProjectInfo);
779

780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800
            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);
801
                }
802

803 804 805 806 807 808 809
                // 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);
                }
810 811 812 813
            }

        // A new project was opened/created, do a full indexing
        } else {
814
            d->m_dirty = true;
815
            filesToReindex.unite(newSourceFiles);
816 817
        }

818
        // Update Project/ProjectInfo and File/ProjectPart table
819
        d->m_projectToProjectsInfo.insert(project, newProjectInfo);
820
        recalculateFileToProjectParts();
821

822 823 824
    } // Mutex scope

    // If requested, dump everything we got
825
    if (DumpProjectInfo)
826
        dumpModelManagerConfiguration(QLatin1String("updateProjectInfo"));
827

828 829 830 831 832
    // Remove files from snapshot that are not reachable any more
    if (filesRemoved)
        GC();

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

834 835 836 837
    // 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.
838
    updateCppEditorDocuments();
839

840 841
    // Trigger reindexing
    return updateSourceFiles(filesToReindex, ForcedProgressNotification);
842 843
}

844 845
ProjectPart::Ptr CppModelManager::projectPartForProjectFile(const QString &projectFile) const
{
846
    return d->m_projectFileToProjectPart.value(projectFile);
847 848
}

849
QList<ProjectPart::Ptr> CppModelManager::projectPart(const Utils::FileName &fileName) const
850
{
851 852
    QMutexLocker locker(&d->m_projectMutex);
    return d->m_fileToProjectParts.value(fileName);
853
}
854

855 856
QList<ProjectPart::Ptr> CppModelManager::projectPartFromDependencies(
        const Utils::FileName &fileName) const
857 858
{
    QSet<ProjectPart::Ptr> parts;
859
    const Utils::FileNameList deps = snapshot().filesDependingOn(fileName);
860

861 862
    QMutexLocker locker(&d->m_projectMutex);
    foreach (const Utils::FileName &dep, deps) {
863
        parts.unite(QSet<ProjectPart::Ptr>::fromList(d->m_fileToProjectParts.value(dep)));
864
    }
865

866
    return parts.values();
867
}
con's avatar
con committed
868

869 870 871 872
ProjectPart::Ptr CppModelManager::fallbackProjectPart() const
{
    ProjectPart::Ptr part(new ProjectPart);

873 874
    part->projectDefines = d->m_definedMacros;
    part->headerPaths = d->m_headerPaths;
875 876
    part->languageVersion = ProjectPart::CXX14;
    part->languageExtensions = ProjectPart::AllExtensions;
877
    part->qtVersion = ProjectPart::Qt5;
878
    part->updateLanguageFeatures();