cppmodelmanager.cpp 41.2 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
3
** Copyright (C) 2013 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

Roberto Raggi's avatar
Roberto Raggi committed
30
#include <cplusplus/pp.h>
31
#include <cplusplus/Overview.h>
con's avatar
con committed
32

33
#include "builtinindexingsupport.h"
con's avatar
con committed
34
#include "cppmodelmanager.h"
35
#include "cppcompletionassist.h"
36 37
#include "cpphighlightingsupport.h"
#include "cpphighlightingsupportinternal.h"
38
#include "cppindexingsupport.h"
39
#include "abstracteditorsupport.h"
40 41 42
#include "cpptoolsconstants.h"
#include "cpptoolseditorsupport.h"
#include "cppfindreferences.h"
con's avatar
con committed
43

44
#include <functional>
45
#include <QtConcurrentRun>
46 47 48 49 50 51 52 53 54 55 56 57 58
#include <QFutureSynchronizer>
#include <utils/runextensions.h>
#include <texteditor/itexteditor.h>
#include <texteditor/basetexteditor.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/session.h>
#include <coreplugin/icore.h>
#include <coreplugin/mimedatabase.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <extensionsystem/pluginmanager.h>
59

60
#include <utils/hostosinfo.h>
hjk's avatar
hjk committed
61 62
#include <utils/qtcassert.h>

con's avatar
con committed
63 64 65 66 67 68 69 70
#include <TranslationUnit.h>
#include <AST.h>
#include <Scope.h>
#include <Literals.h>
#include <Symbols.h>
#include <Names.h>
#include <NameVisitor.h>
#include <TypeVisitor.h>
71
#include <ASTVisitor.h>
con's avatar
con committed
72 73
#include <Lexer.h>
#include <Token.h>
74 75
#include <Parser.h>
#include <Control.h>
76
#include <CoreTypes.h>
con's avatar
con committed
77

78 79 80 81 82
#include <QCoreApplication>
#include <QDebug>
#include <QMutexLocker>
#include <QTime>
#include <QTimer>
83
#include <QtConcurrentMap>
84

85
#include <QTextBlock>
86

87 88
#include <iostream>
#include <sstream>
hjk's avatar
hjk committed
89

90
namespace CPlusPlus {
91

92 93
uint qHash(const CppModelManagerInterface::ProjectPart &p)
{
94
    uint h = qHash(p.defines) ^ p.language ^ p.qtVersion;
95 96 97 98 99 100 101 102 103

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

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

    return h;
}
104

105 106 107 108 109 110 111
bool operator==(const CppModelManagerInterface::ProjectPart &p1,
                const CppModelManagerInterface::ProjectPart &p2)
{
    if (p1.defines != p2.defines)
        return false;
    if (p1.language != p2.language)
        return false;
112
    if (p1.qtVersion!= p2.qtVersion)
113 114 115 116 117
        return false;
    if (p1.includePaths != p2.includePaths)
        return false;
    return p1.frameworkPaths == p2.frameworkPaths;
}
118

119 120
} // namespace CPlusPlus

121 122
using namespace CppTools;
using namespace CppTools::Internal;
con's avatar
con committed
123 124
using namespace CPlusPlus;

125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
#if defined(QTCREATOR_WITH_DUMP_AST) && defined(Q_CC_GNU)

#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
165
#endif // QTCREATOR_WITH_DUMP_AST
166

con's avatar
con committed
167 168 169 170 171 172 173 174
static const char pp_configuration[] =
    "# 1 \"<configuration>\"\n"
    "#define __cplusplus 1\n"
    "#define __extension__\n"
    "#define __context__\n"
    "#define __range__\n"
    "#define   restrict\n"
    "#define __restrict\n"
175
    "#define __restrict__\n"
con's avatar
con committed
176

177 178 179 180
    "#define __complex__\n"
    "#define __imag__\n"
    "#define __real__\n"

181 182
    "#define __builtin_va_arg(a,b) ((b)0)\n"

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

198
CppPreprocessor::CppPreprocessor(QPointer<CppModelManager> modelManager, bool dumpFileNameWhileParsing)
199
    : m_snapshot(modelManager->snapshot()),
200
      m_modelManager(modelManager),
201
      m_dumpFileNameWhileParsing(dumpFileNameWhileParsing),
202
      m_preprocess(this, &m_env),
203
      m_revision(0)
204
{
205
    m_preprocess.setKeepComments(true);
206
}
Roberto Raggi's avatar
Roberto Raggi committed
207 208

CppPreprocessor::~CppPreprocessor()
209
{ }
con's avatar
con committed
210

211 212 213
void CppPreprocessor::setRevision(unsigned revision)
{ m_revision = revision; }

214
void CppPreprocessor::setWorkingCopy(const CppModelManagerInterface::WorkingCopy &workingCopy)
215
{ m_workingCopy = workingCopy; }
con's avatar
con committed
216

217
void CppPreprocessor::setIncludePaths(const QStringList &includePaths)
218 219 220 221
{
    m_includePaths.clear();

    for (int i = 0; i < includePaths.size(); ++i) {
222
        const QString &path = includePaths.at(i);
223

224 225 226 227 228 229 230 231 232 233 234 235
        if (Utils::HostOsInfo::isMacHost()) {
            if (i + 1 < includePaths.size() && path.endsWith(QLatin1String(".framework/Headers"))) {
                const QFileInfo pathInfo(path);
                const QFileInfo frameworkFileInfo(pathInfo.path());
                const QString frameworkName = frameworkFileInfo.baseName();

                const QFileInfo nextIncludePath = includePaths.at(i + 1);
                if (nextIncludePath.fileName() == frameworkName) {
                    // We got a QtXXX.framework/Headers followed by $QTDIR/include/QtXXX.
                    // In this case we prefer to include files from $QTDIR/include/QtXXX.
                    continue;
                }
236 237
            }
        }
238
        m_includePaths.append(cleanPath(path));
239 240
    }
}
241 242

void CppPreprocessor::setFrameworkPaths(const QStringList &frameworkPaths)
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
{
    m_frameworkPaths.clear();

    foreach (const QString &frameworkPath, frameworkPaths) {
        addFrameworkPath(frameworkPath);
    }
}

// Add the given framework path, and expand private frameworks.
//
// Example:
//  <framework-path>/ApplicationServices.framework
// has private frameworks in:
//  <framework-path>/ApplicationServices.framework/Frameworks
// if the "Frameworks" folder exists inside the top level framework.
void CppPreprocessor::addFrameworkPath(const QString &frameworkPath)
{
    // The algorithm below is a bit too eager, but that's because we're not getting
    // in the frameworks we're linking against. If we would have that, then we could
    // add only those private frameworks.
263 264 265
    QString cleanFrameworkPath = cleanPath(frameworkPath);
    if (!m_frameworkPaths.contains(cleanFrameworkPath))
        m_frameworkPaths.append(cleanFrameworkPath);
266

267
    const QDir frameworkDir(cleanFrameworkPath);
268 269 270 271 272
    const QStringList filter = QStringList() << QLatin1String("*.framework");
    foreach (const QFileInfo &framework, frameworkDir.entryInfoList(filter)) {
        if (!framework.isDir())
            continue;
        const QFileInfo privateFrameworks(framework.absoluteFilePath(), QLatin1String("Frameworks"));
273
        if (privateFrameworks.exists() && privateFrameworks.isDir())
274 275 276
            addFrameworkPath(privateFrameworks.absoluteFilePath());
    }
}
con's avatar
con committed
277

278 279 280
void CppPreprocessor::setTodo(const QStringList &files)
{ m_todo = QSet<QString>::fromList(files); }

Wolfgang Beck's avatar
Wolfgang Beck committed
281
namespace {
282
class Process: public std::unary_function<Document::Ptr, void>
283 284
{
    QPointer<CppModelManager> _modelManager;
285
    Document::Ptr _doc;
Roberto Raggi's avatar
Roberto Raggi committed
286
    Document::CheckMode _mode;
287 288

public:
289
    Process(QPointer<CppModelManager> modelManager,
Roberto Raggi's avatar
Roberto Raggi committed
290
            Document::Ptr doc,
291
            const CppModelManager::WorkingCopy &workingCopy)
292
        : _modelManager(modelManager),
Roberto Raggi's avatar
Roberto Raggi committed
293 294
          _doc(doc),
          _mode(Document::FastCheck)
295
    {
296

Roberto Raggi's avatar
Roberto Raggi committed
297 298 299
        if (workingCopy.contains(_doc->fileName()))
            _mode = Document::FullCheck;
    }
300

Roberto Raggi's avatar
Roberto Raggi committed
301 302 303
    void operator()()
    {
        _doc->check(_mode);
304

305
        if (_modelManager)
306
            _modelManager->emitDocumentUpdated(_doc);
307 308

        _doc->releaseSourceAndAST();
309 310 311 312 313 314 315
    }
};
} // end of anonymous namespace

void CppPreprocessor::run(const QString &fileName)
{
    QString absoluteFilePath = fileName;
316
    sourceNeeded(0, absoluteFilePath, IncludeGlobal);
317
}
318

319 320 321 322 323
void CppPreprocessor::removeFromCache(const QString &fileName)
{
    m_snapshot.remove(fileName);
}

324
void CppPreprocessor::resetEnvironment()
325
{
326
    m_env.reset();
327 328
    m_processed.clear();
}
329

330
bool CppPreprocessor::includeFile(const QString &absoluteFilePath, QString *result, unsigned *revision)
331
{
332
    if (absoluteFilePath.isEmpty() || m_included.contains(absoluteFilePath))
333 334 335 336
        return true;

    if (m_workingCopy.contains(absoluteFilePath)) {
        m_included.insert(absoluteFilePath);
337 338 339
        const QPair<QString, unsigned> r = m_workingCopy.get(absoluteFilePath);
        *result = r.first;
        *revision = r.second;
340 341 342 343 344
        return true;
    }

    QFileInfo fileInfo(absoluteFilePath);
    if (! fileInfo.isFile())
con's avatar
con committed
345
        return false;
346 347

    QFile file(absoluteFilePath);
348
    if (file.open(QFile::ReadOnly | QFile::Text)) {
349
        m_included.insert(absoluteFilePath);
350
        QTextCodec *defaultCodec = Core::EditorManager::instance()->defaultTextCodec();
351
        QTextStream stream(&file);
352 353 354
        stream.setCodec(defaultCodec);
        if (result)
            *result = stream.readAll();
355 356
        file.close();
        return true;
con's avatar
con committed
357 358
    }

359 360 361
    return false;
}

362
QString CppPreprocessor::tryIncludeFile(QString &fileName, IncludeType type, unsigned *revision)
Roberto Raggi's avatar
Roberto Raggi committed
363 364 365 366 367 368 369 370 371 372 373 374 375
{
    if (type == IncludeGlobal) {
        const QString fn = m_fileNameCache.value(fileName);

        if (! fn.isEmpty()) {
            fileName = fn;

            if (revision)
                *revision = 0;

            return QString();
        }

376 377
        const QString originalFileName = fileName;
        const QString contents = tryIncludeFile_helper(fileName, type, revision);
Roberto Raggi's avatar
Roberto Raggi committed
378
        m_fileNameCache.insert(originalFileName, fileName);
379 380 381 382 383
        return contents;
    }

    // IncludeLocal, IncludeNext
    return tryIncludeFile_helper(fileName, type, revision);
Roberto Raggi's avatar
Roberto Raggi committed
384 385
}

386
QString CppPreprocessor::cleanPath(const QString &path)
387
{
388 389 390 391 392
    QString result = QDir::cleanPath(path);
    const QChar slash(QLatin1Char('/'));
    if (!result.endsWith(slash))
        result.append(slash);
    return result;
393 394
}

Roberto Raggi's avatar
Roberto Raggi committed
395
QString CppPreprocessor::tryIncludeFile_helper(QString &fileName, IncludeType type, unsigned *revision)
396 397
{
    QFileInfo fileInfo(fileName);
398
    if (fileName == Preprocessor::configurationFileName || fileInfo.isAbsolute()) {
399
        QString contents;
400
        includeFile(fileName, &contents, revision);
401 402 403 404 405
        return contents;
    }

    if (type == IncludeLocal && m_currentDoc) {
        QFileInfo currentFileInfo(m_currentDoc->fileName());
406
        QString path = cleanPath(currentFileInfo.absolutePath()) + fileName;
407
        QString contents;
408
        if (includeFile(path, &contents, revision)) {
409
            fileName = path;
con's avatar
con committed
410 411
            return contents;
        }
412
    }
con's avatar
con committed
413

414
    foreach (const QString &includePath, m_includePaths) {
415
        QString path = includePath + fileName;
416
        QString contents;
417
        if (includeFile(path, &contents, revision)) {
418 419
            fileName = path;
            return contents;
con's avatar
con committed
420
        }
421 422 423 424 425
    }

    int index = fileName.indexOf(QLatin1Char('/'));
    if (index != -1) {
        QString frameworkName = fileName.left(index);
426
        QString name = frameworkName + QLatin1String(".framework/Headers/") + fileName.mid(index + 1);
con's avatar
con committed
427

428
        foreach (const QString &frameworkPath, m_frameworkPaths) {
429
            QString path = frameworkPath + name;
430
            QString contents;
431
            if (includeFile(path, &contents, revision)) {
con's avatar
con committed
432 433 434 435
                fileName = path;
                return contents;
            }
        }
436
    }
con's avatar
con committed
437

438
    //qDebug() << "**** file" << fileName << "not found!";
439
    return QString();
440
}
con's avatar
con committed
441

Roberto Raggi's avatar
Roberto Raggi committed
442
void CppPreprocessor::macroAdded(const Macro &macro)
443 444 445
{
    if (! m_currentDoc)
        return;
con's avatar
con committed
446

Roberto Raggi's avatar
Roberto Raggi committed
447
    m_currentDoc->appendMacro(macro);
448
}
449

450 451 452 453 454 455 456
static inline const Macro revision(const CppModelManagerInterface::WorkingCopy &s, const Macro &macro)
{
    Macro newMacro(macro);
    newMacro.setFileRevision(s.get(macro.fileName()).second);
    return newMacro;
}

457
void CppPreprocessor::passedMacroDefinitionCheck(unsigned offset, unsigned line, const Macro &macro)
Christian Kamm's avatar
Christian Kamm committed
458 459 460 461
{
    if (! m_currentDoc)
        return;

462
    m_currentDoc->addMacroUse(revision(m_workingCopy, macro), offset, macro.name().length(), line,
463
                              QVector<MacroArgumentReference>());
Christian Kamm's avatar
Christian Kamm committed
464 465
}

466
void CppPreprocessor::failedMacroDefinitionCheck(unsigned offset, const ByteArrayRef &name)
Christian Kamm's avatar
Christian Kamm committed
467 468 469 470
{
    if (! m_currentDoc)
        return;

471
    m_currentDoc->addUndefinedMacroUse(QByteArray(name.start(), name.size()), offset);
Christian Kamm's avatar
Christian Kamm committed
472 473
}

474 475 476 477 478
void CppPreprocessor::notifyMacroReference(unsigned offset, unsigned line, const Macro &macro)
{
    if (! m_currentDoc)
        return;

479
    m_currentDoc->addMacroUse(revision(m_workingCopy, macro), offset, macro.name().length(), line,
480 481 482 483
                              QVector<MacroArgumentReference>());
}

void CppPreprocessor::startExpandingMacro(unsigned offset, unsigned line,
Roberto Raggi's avatar
Roberto Raggi committed
484
                                          const Macro &macro,
485
                                          const QVector<MacroArgumentReference> &actuals)
486 487 488
{
    if (! m_currentDoc)
        return;
489

490
    m_currentDoc->addMacroUse(revision(m_workingCopy, macro), offset, macro.name().length(), line, actuals);
491
}
492

493
void CppPreprocessor::stopExpandingMacro(unsigned, const Macro &)
494 495 496
{
    if (! m_currentDoc)
        return;
con's avatar
con committed
497

498 499
    //qDebug() << "stop expanding:" << macro.name;
}
con's avatar
con committed
500

501 502 503 504 505 506 507 508
void CppPreprocessor::markAsIncludeGuard(const QByteArray &macroName)
{
    if (!m_currentDoc)
        return;

    m_currentDoc->setIncludeGuardMacroName(macroName);
}

509 510 511 512
void CppPreprocessor::mergeEnvironment(Document::Ptr doc)
{
    if (! doc)
        return;
con's avatar
con committed
513

514
    const QString fn = doc->fileName();
con's avatar
con committed
515

516
    if (m_processed.contains(fn))
517
        return;
con's avatar
con committed
518

519
    m_processed.insert(fn);
con's avatar
con committed
520

521
    foreach (const Document::Include &incl, doc->includes()) {
522 523
        QString includedFile = incl.fileName();

524
        if (Document::Ptr includedDoc = m_snapshot.document(includedFile))
525
            mergeEnvironment(includedDoc);
526 527
        else
            run(includedFile);
Roberto Raggi's avatar
Roberto Raggi committed
528
    }
con's avatar
con committed
529

530
    m_env.addMacros(doc->definedMacros());
531
}
con's avatar
con committed
532

533 534 535 536 537 538 539 540 541 542 543 544 545 546
void CppPreprocessor::startSkippingBlocks(unsigned offset)
{
    //qDebug() << "start skipping blocks:" << offset;
    if (m_currentDoc)
        m_currentDoc->startSkippingBlocks(offset);
}

void CppPreprocessor::stopSkippingBlocks(unsigned offset)
{
    //qDebug() << "stop skipping blocks:" << offset;
    if (m_currentDoc)
        m_currentDoc->stopSkippingBlocks(offset);
}

547
void CppPreprocessor::sourceNeeded(unsigned line, QString &fileName, IncludeType type)
548 549 550 551
{
    if (fileName.isEmpty())
        return;

552 553
    unsigned editorRevision = 0;
    QString contents = tryIncludeFile(fileName, type, &editorRevision);
554
    fileName = QDir::cleanPath(fileName);
555
    if (m_currentDoc) {
556
        m_currentDoc->addIncludeFile(fileName, line);
557

558
        if (contents.isEmpty() && ! QFileInfo(fileName).isAbsolute()) {
559 560
            QString msg = QCoreApplication::translate(
                    "CppPreprocessor", "%1: No such file or directory").arg(fileName);
561

562 563
            Document::DiagnosticMessage d(Document::DiagnosticMessage::Warning,
                                          m_currentDoc->fileName(),
564
                                          line, /*column = */ 0,
565
                                          msg);
566

567
            m_currentDoc->addDiagnosticMessage(d);
568

569
            //qWarning() << "file not found:" << fileName << m_currentDoc->fileName() << env.current_line;
con's avatar
con committed
570
        }
571
    }
con's avatar
con committed
572

573 574
    if (m_dumpFileNameWhileParsing) {
        qDebug() << "Parsing file:" << fileName
575
//             << "contents:" << contents.size()
576 577
                    ;
    }
con's avatar
con committed
578

579
    Document::Ptr doc = m_snapshot.document(fileName);
580 581 582 583
    if (doc) {
        mergeEnvironment(doc);
        return;
    }
con's avatar
con committed
584

585
    doc = Document::create(fileName);
586
    doc->setRevision(m_revision);
587
    doc->setEditorRevision(editorRevision);
con's avatar
con committed
588

589 590 591 592
    QFileInfo info(fileName);
    if (info.exists())
        doc->setLastModified(info.lastModified());

593
    Document::Ptr previousDoc = switchDocument(doc);
con's avatar
con committed
594

595
    const QByteArray preprocessedCode = m_preprocess.run(fileName, contents);
con's avatar
con committed
596

597 598
//    { QByteArray b(preprocessedCode); b.replace("\n", "<<<\n"); qDebug("Preprocessed code for \"%s\": [[%s]]", fileName.toUtf8().constData(), b.constData()); }

599
    doc->setUtf8Source(preprocessedCode);
600
    doc->keepSourceAndAST();
601
    doc->tokenize();
602

603
    m_snapshot.insert(doc);
604
    m_todo.remove(fileName);
con's avatar
con committed
605

606
    Process process(m_modelManager, doc, m_workingCopy);
Roberto Raggi's avatar
Roberto Raggi committed
607
    process();
608 609

    (void) switchDocument(previousDoc);
610
}
con's avatar
con committed
611

612 613 614 615 616 617
Document::Ptr CppPreprocessor::switchDocument(Document::Ptr doc)
{
    Document::Ptr previousDoc = m_currentDoc;
    m_currentDoc = doc;
    return previousDoc;
}
con's avatar
con committed
618

619
void CppModelManager::updateModifiedSourceFiles()
620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637
{
    const Snapshot snapshot = this->snapshot();
    QStringList sourceFiles;

    foreach (const Document::Ptr doc, snapshot) {
        const QDateTime lastModified = doc->lastModified();

        if (! lastModified.isNull()) {
            QFileInfo fileInfo(doc->fileName());

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

    updateSourceFiles(sourceFiles);
}

con's avatar
con committed
638 639 640 641 642 643
/*!
    \class CppTools::CppModelManager
    \brief The CppModelManager keeps track of one CppCodeModel instance
           for each project and all related CppCodeModelPart instances.

    It also takes care of updating the code models when C++ files are
644
    modified within Qt Creator.
con's avatar
con committed
645 646
*/

647 648 649 650 651 652 653 654
QMutex CppModelManager::m_modelManagerMutex;
CppModelManager *CppModelManager::m_modelManagerInstance = 0;

CppModelManager *CppModelManager::instance()
{
    if (m_modelManagerInstance)
        return m_modelManagerInstance;
    QMutexLocker locker(&m_modelManagerMutex);
655
    if (!m_modelManagerInstance)
656 657 658 659
        m_modelManagerInstance = new CppModelManager;
    return m_modelManagerInstance;
}

660
CppModelManager::CppModelManager(QObject *parent)
661
    : CppModelManagerInterface(parent)
662 663 664
    , m_completionAssistProvider(0)
    , m_highlightingFactory(0)
    , m_indexingSupporter(0)
con's avatar
con committed
665
{
Roberto Raggi's avatar
Roberto Raggi committed
666
    m_findReferences = new CppFindReferences(this);
667
    m_indexerEnabled = qgetenv("QTCREATOR_NO_CODE_INDEXER").isNull();
Roberto Raggi's avatar
Roberto Raggi committed
668

669 670
    m_dirty = true;

671
    ProjectExplorer::ProjectExplorerPlugin *pe =
672
       ProjectExplorer::ProjectExplorerPlugin::instance();
con's avatar
con committed
673

674
    QTC_ASSERT(pe, return);
con's avatar
con committed
675

676
    ProjectExplorer::SessionManager *session = pe->session();
Roberto Raggi's avatar
Roberto Raggi committed
677 678 679 680 681 682
    m_updateEditorSelectionsTimer = new QTimer(this);
    m_updateEditorSelectionsTimer->setInterval(500);
    m_updateEditorSelectionsTimer->setSingleShot(true);
    connect(m_updateEditorSelectionsTimer, SIGNAL(timeout()),
            this, SLOT(updateEditorSelections()));

683 684 685
    connect(session, SIGNAL(projectAdded(ProjectExplorer::Project*)),
            this, SLOT(onProjectAdded(ProjectExplorer::Project*)));

Robert Loehning's avatar
Robert Loehning committed
686 687
    connect(session, SIGNAL(aboutToRemoveProject(ProjectExplorer::Project*)),
            this, SLOT(onAboutToRemoveProject(ProjectExplorer::Project*)));
con's avatar
con committed
688

689
    connect(session, SIGNAL(aboutToUnloadSession(QString)),
690
            this, SLOT(onAboutToUnloadSession()));
con's avatar
con committed
691 692 693 694 695 696

    qRegisterMetaType<CPlusPlus::Document::Ptr>("CPlusPlus::Document::Ptr");

    // thread connections
    connect(this, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)),
            this, SLOT(onDocumentUpdated(CPlusPlus::Document::Ptr)));
697 698
    connect(this, SIGNAL(extraDiagnosticsUpdated(QString)),
            this, SLOT(onExtraDiagnosticsUpdated(QString)));
con's avatar
con committed
699 700

    // Listen for editor closed and opened events so that we can keep track of changing files
Robert Loehning's avatar
Robert Loehning committed
701 702
    connect(Core::ICore::editorManager(), SIGNAL(editorOpened(Core::IEditor*)),
        this, SLOT(editorOpened(Core::IEditor*)));
con's avatar
con committed
703

Robert Loehning's avatar
Robert Loehning committed
704 705
    connect(Core::ICore::editorManager(), SIGNAL(editorAboutToClose(Core::IEditor*)),
        this, SLOT(editorAboutToClose(Core::IEditor*)));
706

707 708
    m_completionFallback = new InternalCompletionAssistProvider;
    m_completionAssistProvider = m_completionFallback;
709
    ExtensionSystem::PluginManager::addObject(m_completionAssistProvider);
710 711
    m_highlightingFallback = new CppHighlightingSupportInternalFactory;
    m_highlightingFactory = m_highlightingFallback;
712
    m_internalIndexingSupport = new BuiltinIndexingSupport;
con's avatar
con committed
713 714 715
}

CppModelManager::~CppModelManager()
716
{
717
    ExtensionSystem::PluginManager::removeObject(m_completionAssistProvider);
718 719
    delete m_completionFallback;
    delete m_highlightingFallback;
720
    delete m_internalIndexingSupport;
721
}
con's avatar
con committed
722

723
Snapshot CppModelManager::snapshot() const
724
{
725
    QMutexLocker locker(&protectSnapshot);
726 727
    return m_snapshot;
}
con's avatar
con committed
728

729 730 731 732 733 734
void CppModelManager::ensureUpdated()
{
    QMutexLocker locker(&mutex);
    if (! m_dirty)
        return;

735 736 737 738
    m_projectFiles = internalProjectFiles();
    m_includePaths = internalIncludePaths();
    m_frameworkPaths = internalFrameworkPaths();
    m_definedMacros = internalDefinedMacros();
739 740 741
    m_dirty = false;
}

742
QStringList CppModelManager::internalProjectFiles() const
con's avatar
con committed
743 744 745 746 747 748
{
    QStringList files;
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects);
    while (it.hasNext()) {
        it.next();
        ProjectInfo pinfo = it.value();
749 750
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
            files += part->headerFiles;
751
            files += part->sourceFiles;
752 753
            files += part->objcSourceFiles;
        }
con's avatar
con committed
754
    }
755
    files.removeDuplicates();
con's avatar
con committed
756 757 758
    return files;
}

759
QStringList CppModelManager::internalIncludePaths() const
con's avatar
con committed
760 761 762 763 764 765
{
    QStringList includePaths;
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects);
    while (it.hasNext()) {
        it.next();
        ProjectInfo pinfo = it.value();
766
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts())
767 768
            foreach (const QString &path, part->includePaths)
                includePaths.append(CppPreprocessor::cleanPath(path));
con's avatar
con committed
769
    }
770
    includePaths.removeDuplicates();
con's avatar
con committed
771 772 773
    return includePaths;
}

774
QStringList CppModelManager::internalFrameworkPaths() const
con's avatar
con committed
775 776 777 778 779 780
{
    QStringList frameworkPaths;
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects);
    while (it.hasNext()) {
        it.next();
        ProjectInfo pinfo = it.value();
781
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts())
782 783
            foreach (const QString &path, part->frameworkPaths)
                frameworkPaths.append(CppPreprocessor::cleanPath(path));
con's avatar
con committed
784
    }
785
    frameworkPaths.removeDuplicates();
con's avatar
con committed
786 787 788
    return frameworkPaths;
}

789
QByteArray CppModelManager::internalDefinedMacros() const
con's avatar
con committed
790 791
{
    QByteArray macros;
792
    QSet<QByteArray> alreadyIn;
con's avatar
con committed
793 794 795 796
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects);
    while (it.hasNext()) {
        it.next();
        ProjectInfo pinfo = it.value();
797 798 799 800 801 802 803 804 805 806
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
            const QList<QByteArray> defs = part->defines.split('\n');
            foreach (const QByteArray &def, defs) {
                if (!alreadyIn.contains(def)) {
                    macros += def;
                    macros.append('\n');
                    alreadyIn.insert(def);
                }
            }
        }
con's avatar
con committed
807 808 809 810
    }
    return macros;
}

811 812 813 814 815
/// This method will aquire the mutex!
void CppModelManager::dumpModelManagerConfiguration()
{
    // Tons of debug output...
    qDebug()<<"========= CppModelManager::dumpModelManagerConfiguration ======";
816
    foreach (const ProjectInfo &pinfo, m_projects) {
817 818 819
        qDebug()<<" for project:"<< pinfo.project().data()->document()->fileName();
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
            qDebug() << "=== part ===";
820 821 822 823 824 825 826 827 828 829
            const char* lang;
            switch (part->language) {
            case ProjectPart::CXX: lang = "C++"; break;
            case ProjectPart::CXX11: lang = "C++11"; break;
            case ProjectPart::C89: lang = "C89"; break;
            case ProjectPart::C99: lang = "C99"; break;
            default: lang = "INVALID";
            }

            qDebug() << "language:" << lang;
830 831 832 833 834
            qDebug() << "Qt version:" << part->qtVersion;
            qDebug() << "precompiled header:" << part->precompiledHeaders;
            qDebug() << "defines:" << part->defines;
            qDebug() << "includes:" << part->includePaths;
            qDebug() << "frameworkPaths:" << part->frameworkPaths;
835
            qDebug() << "headers:" << part->headerFiles;
836
            qDebug() << "sources:" << part->sourceFiles;
837
            qDebug() << "objc sources:" << part->objcSourceFiles;
838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853
            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;
    qDebug()<<"========= End of dump ======";
}

854 855 856 857 858 859 860 861 862 863
void CppModelManager::addEditorSupport(AbstractEditorSupport *editorSupport)
{
    m_addtionalEditorSupport.insert(editorSupport);
}

void CppModelManager::removeEditorSupport(AbstractEditorSupport *editorSupport)
{
    m_addtionalEditorSupport.remove(editorSupport);
}

864
QList<int> CppModelManager::references(CPlusPlus::Symbol *symbol, const LookupContext &context)
865
{
866
    return m_findReferences->references(symbol, context);
867 868
}

869
void CppModelManager::findUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context)
Roberto Raggi's avatar
Roberto Raggi committed
870
{
871
    if (symbol->identifier())
872
        m_findReferences->findUsages(symbol, context);
873 874
}

875 876
void CppModelManager::renameUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context,
                                   const QString &replacement)
877 878
{
    if (symbol->identifier())
879
        m_findReferences->renameUsages(symbol, context, replacement);
Roberto Raggi's avatar
Roberto Raggi committed
880 881
}

Christian Kamm's avatar
Christian Kamm committed
882 883 884 885 886
void CppModelManager::findMacroUsages(const CPlusPlus::Macro &macro)
{
    m_findReferences->findMacroUses(macro);
}

887 888 889 890 891
void CppModelManager::renameMacroUsages(const CPlusPlus::Macro &macro, const QString &replacement)
{
    m_findReferences->renameMacroUses(macro, replacement);
}

892
CppModelManager::WorkingCopy CppModelManager::buildWorkingCopyList()
con's avatar
con committed
893
{
894
    WorkingCopy workingCopy;
con's avatar
con committed
895 896 897 898 899
    QMapIterator<TextEditor::ITextEditor *, CppEditorSupport *> it(m_editorSupport);
    while (it.hasNext()) {
        it.next();
        TextEditor::ITextEditor *textEditor = it.key();
        CppEditorSupport *editorSupport = it.value();
900
        QString fileName = textEditor->document()->fileName();
901
        workingCopy.insert(fileName, editorSupport->contents(), editorSupport->editorRevision());
con's avatar
con committed
902 903
    }

904 905 906
    QSetIterator<AbstractEditorSupport *> jt(m_addtionalEditorSupport);
    while (jt.hasNext()) {
        AbstractEditorSupport *es =  jt.next();
907
        workingCopy.insert(es->fileName(), QString::fromUtf8(es->contents()));
908 909
    }

con's avatar
con committed
910 911 912
    // add the project configuration file
    QByteArray conf(pp_configuration);
    conf += definedMacros();
913
    workingCopy.insert(configurationFileName(), QString::fromUtf8(conf));
con's avatar
con committed
914 915 916 917

    return workingCopy;
}

918
CppModelManager::WorkingCopy CppModelManager::workingCopy() const
919 920 921 922
{
    return const_cast<CppModelManager *>(this)->buildWorkingCopyList();
}

dt's avatar
dt committed
923
QFuture<void> CppModelManager::updateSourceFiles(const QStringList &sourceFiles)
924 925 926 927
{
    if (sourceFiles.isEmpty() || !m_indexerEnabled)
        return QFuture<void>();

928 929
    if (m_indexingSupporter)
        m_indexingSupporter->refreshSourceFiles(sourceFiles);
930 931
    return m_internalIndexingSupport->refreshSourceFiles(sourceFiles);
}
con's avatar
con committed
932

933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948
QList<CppModelManager::ProjectInfo> CppModelManager::projectInfos() const
{
    QMutexLocker locker(&mutex);

    return m_projects.values();
}

CppModelManager::ProjectInfo CppModelManager::projectInfo(ProjectExplorer::Project *project) const
{
    QMutexLocker locker(&mutex);

    return m_projects.value(project, ProjectInfo(project));
}

void CppModelManager::updateProjectInfo(const ProjectInfo &pinfo)
{
949