cppmodelmanager.cpp 43.1 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
hjk's avatar
hjk committed
3 4
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
** 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 "cppmodelmanager.h"
34
#include "cppcompletionassist.h"
35 36
#include "cpphighlightingsupport.h"
#include "cpphighlightingsupportinternal.h"
37
#include "abstracteditorsupport.h"
Wolfgang Beck's avatar
Wolfgang Beck committed
38 39 40 41 42
#ifndef ICHECK_BUILD
#  include "cpptoolsconstants.h"
#  include "cpptoolseditorsupport.h"
#  include "cppfindreferences.h"
#endif
con's avatar
con committed
43

44
#include <functional>
45
#include <QtConcurrentRun>
Wolfgang Beck's avatar
Wolfgang Beck committed
46
#ifndef ICHECK_BUILD
47
#  include <QFutureSynchronizer>
48
#  include <utils/runextensions.h>
Wolfgang Beck's avatar
Wolfgang Beck committed
49 50 51 52 53 54 55 56 57 58 59 60
#  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>
#else
61
#  include <QDir>
Wolfgang Beck's avatar
Wolfgang Beck committed
62
#endif
63

hjk's avatar
hjk committed
64 65
#include <utils/qtcassert.h>

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

81 82 83 84 85
#include <QCoreApplication>
#include <QDebug>
#include <QMutexLocker>
#include <QTime>
#include <QTimer>
86
#include <QtConcurrentMap>
87

88
#include <QTextBlock>
89

90 91
#include <iostream>
#include <sstream>
hjk's avatar
hjk committed
92

93 94 95
namespace CPlusPlus {
uint qHash(const CppModelManagerInterface::ProjectPart &p)
{
96
    uint h = qHash(p.defines) ^ p.language ^ ((int) p.cxx11Enabled);
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112

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

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

    return h;
}
bool operator==(const CppModelManagerInterface::ProjectPart &p1,
                const CppModelManagerInterface::ProjectPart &p2)
{
    if (p1.defines != p2.defines)
        return false;
    if (p1.language != p2.language)
        return false;
113
    if (p1.cxx11Enabled != p2.cxx11Enabled)
114 115 116 117 118 119 120
        return false;
    if (p1.includePaths != p2.includePaths)
        return false;
    return p1.frameworkPaths == p2.frameworkPaths;
}
} // 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 175 176
static const char pp_configuration_file[] = "<configuration>";

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"
177
    "#define __restrict__\n"
con's avatar
con committed
178

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

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

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

200
#ifndef ICHECK_BUILD
201
CppPreprocessor::CppPreprocessor(QPointer<CppModelManager> modelManager, bool dumpFileNameWhileParsing)
202 203
    : snapshot(modelManager->snapshot()),
      m_modelManager(modelManager),
204
      m_dumpFileNameWhileParsing(dumpFileNameWhileParsing),
205 206
      preprocess(this, &env),
      m_revision(0)
207 208 209
{
    preprocess.setKeepComments(true);
}
Roberto Raggi's avatar
Roberto Raggi committed
210

211 212 213 214
#else

CppPreprocessor::CppPreprocessor(QPointer<CPlusPlus::ParseManager> modelManager)
    : preprocess(this, &env),
215
      m_dumpFileNameWhileParsing(false),
216 217 218 219 220
      m_revision(0)
{
}
#endif

Roberto Raggi's avatar
Roberto Raggi committed
221
CppPreprocessor::~CppPreprocessor()
222
{ }
con's avatar
con committed
223

224 225 226
void CppPreprocessor::setRevision(unsigned revision)
{ m_revision = revision; }

227
void CppPreprocessor::setWorkingCopy(const CppModelManagerInterface::WorkingCopy &workingCopy)
228
{ m_workingCopy = workingCopy; }
con's avatar
con committed
229

230
void CppPreprocessor::setIncludePaths(const QStringList &includePaths)
231 232 233 234
{
    m_includePaths.clear();

    for (int i = 0; i < includePaths.size(); ++i) {
235
        const QString &path = includePaths.at(i);
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255

#ifdef Q_OS_DARWIN
        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;
            }
        }
        m_includePaths.append(path);
#else
        m_includePaths.append(path);
#endif
    }
}
256 257

void CppPreprocessor::setFrameworkPaths(const QStringList &frameworkPaths)
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
{
    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.
    if (!m_frameworkPaths.contains(frameworkPath)) {
        m_frameworkPaths.append(frameworkPath);
    }

    const QDir frameworkDir(frameworkPath);
    const QStringList filter = QStringList() << QLatin1String("*.framework");
    foreach (const QFileInfo &framework, frameworkDir.entryInfoList(filter)) {
        if (!framework.isDir())
            continue;
        const QFileInfo privateFrameworks(framework.absoluteFilePath(), QLatin1String("Frameworks"));
        if (privateFrameworks.exists() && privateFrameworks.isDir()) {
            addFrameworkPath(privateFrameworks.absoluteFilePath());
        }
    }
}
con's avatar
con committed
293

294 295 296
void CppPreprocessor::setProjectFiles(const QStringList &files)
{ m_projectFiles = files; }

297 298 299
void CppPreprocessor::setTodo(const QStringList &files)
{ m_todo = QSet<QString>::fromList(files); }

300
#ifndef ICHECK_BUILD
Wolfgang Beck's avatar
Wolfgang Beck committed
301
namespace {
302
class Process: public std::unary_function<Document::Ptr, void>
303 304
{
    QPointer<CppModelManager> _modelManager;
305 306
    Snapshot _snapshot;
    Document::Ptr _doc;
Roberto Raggi's avatar
Roberto Raggi committed
307
    Document::CheckMode _mode;
308 309

public:
310
    Process(QPointer<CppModelManager> modelManager,
Roberto Raggi's avatar
Roberto Raggi committed
311 312
            Document::Ptr doc,
            const Snapshot &snapshot,
313
            const CppModelManager::WorkingCopy &workingCopy)
314 315
        : _modelManager(modelManager),
          _snapshot(snapshot),
Roberto Raggi's avatar
Roberto Raggi committed
316 317
          _doc(doc),
          _mode(Document::FastCheck)
318
    {
319

Roberto Raggi's avatar
Roberto Raggi committed
320 321 322
        if (workingCopy.contains(_doc->fileName()))
            _mode = Document::FullCheck;
    }
323

Roberto Raggi's avatar
Roberto Raggi committed
324 325 326
    void operator()()
    {
        _doc->check(_mode);
327

328
        if (_modelManager)
Roberto Raggi's avatar
Roberto Raggi committed
329
            _modelManager->emitDocumentUpdated(_doc); // ### TODO: compress
330 331

        _doc->releaseSourceAndAST();
332 333 334
    }
};
} // end of anonymous namespace
Wolfgang Beck's avatar
Wolfgang Beck committed
335
#endif
336 337 338 339

void CppPreprocessor::run(const QString &fileName)
{
    QString absoluteFilePath = fileName;
340
    sourceNeeded(0, absoluteFilePath, IncludeGlobal);
341
}
342

343
void CppPreprocessor::resetEnvironment()
344 345 346 347
{
    env.reset();
    m_processed.clear();
}
348

349
bool CppPreprocessor::includeFile(const QString &absoluteFilePath, QString *result, unsigned *revision)
350
{
351
    if (absoluteFilePath.isEmpty() || m_included.contains(absoluteFilePath))
352 353 354 355
        return true;

    if (m_workingCopy.contains(absoluteFilePath)) {
        m_included.insert(absoluteFilePath);
356 357 358
        const QPair<QString, unsigned> r = m_workingCopy.get(absoluteFilePath);
        *result = r.first;
        *revision = r.second;
359 360 361 362 363
        return true;
    }

    QFileInfo fileInfo(absoluteFilePath);
    if (! fileInfo.isFile())
con's avatar
con committed
364
        return false;
365 366

    QFile file(absoluteFilePath);
367
    if (file.open(QFile::ReadOnly | QFile::Text)) {
368 369 370 371 372 373
        m_included.insert(absoluteFilePath);
        QTextStream stream(&file);
        const QString contents = stream.readAll();
        *result = contents.toUtf8();
        file.close();
        return true;
con's avatar
con committed
374 375
    }

376 377 378
    return false;
}

379
QString CppPreprocessor::tryIncludeFile(QString &fileName, IncludeType type, unsigned *revision)
Roberto Raggi's avatar
Roberto Raggi committed
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400
{
    if (type == IncludeGlobal) {
        const QString fn = m_fileNameCache.value(fileName);

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

            if (revision)
                *revision = 0;

            return QString();
        }
    }

    const QString originalFileName = fileName;
    const QString contents = tryIncludeFile_helper(fileName, type, revision);
    if (type == IncludeGlobal)
        m_fileNameCache.insert(originalFileName, fileName);
    return contents;
}

401 402 403 404 405 406
static inline void appendDirSeparatorIfNeeded(QString &path)
{
    if (!path.endsWith(QLatin1Char('/'), Qt::CaseInsensitive))
        path += QLatin1Char('/');
}

Roberto Raggi's avatar
Roberto Raggi committed
407
QString CppPreprocessor::tryIncludeFile_helper(QString &fileName, IncludeType type, unsigned *revision)
408 409 410
{
    QFileInfo fileInfo(fileName);
    if (fileName == QLatin1String(pp_configuration_file) || fileInfo.isAbsolute()) {
411
        QString contents;
412
        includeFile(fileName, &contents, revision);
413 414 415 416 417 418
        return contents;
    }

    if (type == IncludeLocal && m_currentDoc) {
        QFileInfo currentFileInfo(m_currentDoc->fileName());
        QString path = currentFileInfo.absolutePath();
419
        appendDirSeparatorIfNeeded(path);
420 421
        path += fileName;
        path = QDir::cleanPath(path);
422
        QString contents;
423
        if (includeFile(path, &contents, revision)) {
424
            fileName = path;
con's avatar
con committed
425 426
            return contents;
        }
427
    }
con's avatar
con committed
428

429 430
    foreach (const QString &includePath, m_includePaths) {
        QString path = includePath;
431
        appendDirSeparatorIfNeeded(path);
432 433
        path += fileName;
        path = QDir::cleanPath(path);
434
        QString contents;
435
        if (includeFile(path, &contents, revision)) {
436 437
            fileName = path;
            return contents;
con's avatar
con committed
438
        }
439
    }
con's avatar
con committed
440

441 442 443
    // look in the system include paths
    foreach (const QString &includePath, m_systemIncludePaths) {
        QString path = includePath;
444
        appendDirSeparatorIfNeeded(path);
445 446
        path += fileName;
        path = QDir::cleanPath(path);
447
        QString contents;
448
        if (includeFile(path, &contents, revision)) {
449 450
            fileName = path;
            return contents;
con's avatar
con committed
451
        }
452 453 454 455 456 457
    }

    int index = fileName.indexOf(QLatin1Char('/'));
    if (index != -1) {
        QString frameworkName = fileName.left(index);
        QString name = fileName.mid(index + 1);
con's avatar
con committed
458

459 460
        foreach (const QString &frameworkPath, m_frameworkPaths) {
            QString path = frameworkPath;
461
            appendDirSeparatorIfNeeded(path);
462 463 464
            path += frameworkName;
            path += QLatin1String(".framework/Headers/");
            path += name;
465
            path = QDir::cleanPath(path);
466
            QString contents;
467
            if (includeFile(path, &contents, revision)) {
con's avatar
con committed
468 469 470 471
                fileName = path;
                return contents;
            }
        }
472
    }
con's avatar
con committed
473

474 475 476
    QString path = fileName;
    if (path.at(0) != QLatin1Char('/'))
        path.prepend(QLatin1Char('/'));
con's avatar
con committed
477

478 479 480
    foreach (const QString &projectFile, m_projectFiles) {
        if (projectFile.endsWith(path)) {
            fileName = projectFile;
481
            QString contents;
482
            includeFile(fileName, &contents, revision);
483
            return contents;
con's avatar
con committed
484 485 486
        }
    }

487
    //qDebug() << "**** file" << fileName << "not found!";
488
    return QString();
489
}
con's avatar
con committed
490

Roberto Raggi's avatar
Roberto Raggi committed
491
void CppPreprocessor::macroAdded(const Macro &macro)
492 493 494
{
    if (! m_currentDoc)
        return;
con's avatar
con committed
495

Roberto Raggi's avatar
Roberto Raggi committed
496
    m_currentDoc->appendMacro(macro);
497
}
498

499
void CppPreprocessor::passedMacroDefinitionCheck(unsigned offset, unsigned line, const Macro &macro)
Christian Kamm's avatar
Christian Kamm committed
500 501 502 503
{
    if (! m_currentDoc)
        return;

504
    m_currentDoc->addMacroUse(macro, offset, macro.name().length(), line,
505
                              QVector<MacroArgumentReference>());
Christian Kamm's avatar
Christian Kamm committed
506 507
}

508
void CppPreprocessor::failedMacroDefinitionCheck(unsigned offset, const ByteArrayRef &name)
Christian Kamm's avatar
Christian Kamm committed
509 510 511 512
{
    if (! m_currentDoc)
        return;

513
    m_currentDoc->addUndefinedMacroUse(QByteArray(name.start(), name.size()), offset);
Christian Kamm's avatar
Christian Kamm committed
514 515
}

516 517 518 519 520 521 522 523 524 525
void CppPreprocessor::notifyMacroReference(unsigned offset, unsigned line, const Macro &macro)
{
    if (! m_currentDoc)
        return;

    m_currentDoc->addMacroUse(macro, offset, macro.name().length(), line,
                              QVector<MacroArgumentReference>());
}

void CppPreprocessor::startExpandingMacro(unsigned offset, unsigned line,
Roberto Raggi's avatar
Roberto Raggi committed
526
                                          const Macro &macro,
527
                                          const QVector<MacroArgumentReference> &actuals)
528 529 530
{
    if (! m_currentDoc)
        return;
531

532
    m_currentDoc->addMacroUse(macro, offset, macro.name().length(), line, actuals);
533
}
534

535
void CppPreprocessor::stopExpandingMacro(unsigned, const Macro &)
536 537 538
{
    if (! m_currentDoc)
        return;
con's avatar
con committed
539

540 541
    //qDebug() << "stop expanding:" << macro.name;
}
con's avatar
con committed
542

543 544 545 546
void CppPreprocessor::mergeEnvironment(Document::Ptr doc)
{
    if (! doc)
        return;
con's avatar
con committed
547

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

550
    if (m_processed.contains(fn))
551
        return;
con's avatar
con committed
552

553
    m_processed.insert(fn);
con's avatar
con committed
554

555
    foreach (const Document::Include &incl, doc->includes()) {
556 557
        QString includedFile = incl.fileName();

558
        if (Document::Ptr includedDoc = snapshot.document(includedFile))
559
            mergeEnvironment(includedDoc);
560 561
        else
            run(includedFile);
Roberto Raggi's avatar
Roberto Raggi committed
562
    }
con's avatar
con committed
563

564
    env.addMacros(doc->definedMacros());
565
}
con's avatar
con committed
566

567 568 569 570 571 572 573 574 575 576 577 578 579 580
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);
}

581
void CppPreprocessor::sourceNeeded(unsigned line, QString &fileName, IncludeType type)
582 583 584 585
{
    if (fileName.isEmpty())
        return;

586 587
    unsigned editorRevision = 0;
    QString contents = tryIncludeFile(fileName, type, &editorRevision);
588
    fileName = QDir::cleanPath(fileName);
589
    if (m_currentDoc) {
590
        m_currentDoc->addIncludeFile(fileName, line);
591

592
        if (contents.isEmpty() && ! QFileInfo(fileName).isAbsolute()) {
593 594
            QString msg = QCoreApplication::translate(
                    "CppPreprocessor", "%1: No such file or directory").arg(fileName);
595

596 597
            Document::DiagnosticMessage d(Document::DiagnosticMessage::Warning,
                                          m_currentDoc->fileName(),
598
                                          line, /*column = */ 0,
599
                                          msg);
600

601
            m_currentDoc->addDiagnosticMessage(d);
602

603
            //qWarning() << "file not found:" << fileName << m_currentDoc->fileName() << env.current_line;
con's avatar
con committed
604
        }
605
    }
con's avatar
con committed
606

607 608
    if (m_dumpFileNameWhileParsing) {
        qDebug() << "Parsing file:" << fileName
609
//             << "contents:" << contents.size()
610 611
                    ;
    }
con's avatar
con committed
612

613
    Document::Ptr doc = snapshot.document(fileName);
614 615 616 617
    if (doc) {
        mergeEnvironment(doc);
        return;
    }
con's avatar
con committed
618

619
    doc = Document::create(fileName);
620
    doc->setRevision(m_revision);
621
    doc->setEditorRevision(editorRevision);
con's avatar
con committed
622

623 624 625 626
    QFileInfo info(fileName);
    if (info.exists())
        doc->setLastModified(info.lastModified());

627
    Document::Ptr previousDoc = switchDocument(doc);
con's avatar
con committed
628

629
    const QByteArray preprocessedCode = preprocess.run(fileName, contents);
con's avatar
con committed
630

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

633
    doc->setUtf8Source(preprocessedCode);
634
    doc->keepSourceAndAST();
635
    doc->tokenize();
636

637
    snapshot.insert(doc);
638
    m_todo.remove(fileName);
con's avatar
con committed
639

640
#ifndef ICHECK_BUILD
Roberto Raggi's avatar
Roberto Raggi committed
641
    Process process(m_modelManager, doc, snapshot, m_workingCopy);
642

Roberto Raggi's avatar
Roberto Raggi committed
643
    process();
644 645

    (void) switchDocument(previousDoc);
646
#else
647
    doc->releaseSource();
648 649 650 651
    Document::CheckMode mode = Document::FastCheck;
    mode = Document::FullCheck;
    doc->parse();
    doc->check(mode);
Erik Verbruggen's avatar
Erik Verbruggen committed
652 653

    (void) switchDocument(previousDoc);
654
#endif
655
}
con's avatar
con committed
656

657 658 659 660 661 662
Document::Ptr CppPreprocessor::switchDocument(Document::Ptr doc)
{
    Document::Ptr previousDoc = m_currentDoc;
    m_currentDoc = doc;
    return previousDoc;
}
con's avatar
con committed
663

664
#ifndef ICHECK_BUILD
665
void CppModelManager::updateModifiedSourceFiles()
666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683
{
    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);
}

684
CppModelManager *CppModelManager::instance()
685
{
686 687
    // TODO this is pretty stupid. use regular singleton pattern.
    return ExtensionSystem::PluginManager::getObject<CppModelManager>();
688 689
}

con's avatar
con committed
690 691 692 693 694 695 696

/*!
    \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
697
    modified within Qt Creator.
con's avatar
con committed
698 699
*/

700
CppModelManager::CppModelManager(QObject *parent)
701
    : CppModelManagerInterface(parent)
con's avatar
con committed
702
{
Roberto Raggi's avatar
Roberto Raggi committed
703
    m_findReferences = new CppFindReferences(this);
704
    m_indexerEnabled = qgetenv("QTCREATOR_NO_CODE_INDEXER").isNull();
705
    m_dumpFileNameWhileParsing = !qgetenv("QTCREATOR_DUMP_FILENAME_WHILE_PARSING").isNull();
Roberto Raggi's avatar
Roberto Raggi committed
706

707
    m_revision = 0;
Roberto Raggi's avatar
Roberto Raggi committed
708 709
    m_synchronizer.setCancelOnWait(true);

710 711
    m_dirty = true;

712
    ProjectExplorer::ProjectExplorerPlugin *pe =
713
       ProjectExplorer::ProjectExplorerPlugin::instance();
con's avatar
con committed
714

715
    QTC_ASSERT(pe, return);
con's avatar
con committed
716

717
    ProjectExplorer::SessionManager *session = pe->session();
Roberto Raggi's avatar
Roberto Raggi committed
718 719 720 721 722 723
    m_updateEditorSelectionsTimer = new QTimer(this);
    m_updateEditorSelectionsTimer->setInterval(500);
    m_updateEditorSelectionsTimer->setSingleShot(true);
    connect(m_updateEditorSelectionsTimer, SIGNAL(timeout()),
            this, SLOT(updateEditorSelections()));

724 725 726
    connect(session, SIGNAL(projectAdded(ProjectExplorer::Project*)),
            this, SLOT(onProjectAdded(ProjectExplorer::Project*)));

Robert Loehning's avatar
Robert Loehning committed
727 728
    connect(session, SIGNAL(aboutToRemoveProject(ProjectExplorer::Project*)),
            this, SLOT(onAboutToRemoveProject(ProjectExplorer::Project*)));
con's avatar
con committed
729

730
    connect(session, SIGNAL(aboutToUnloadSession(QString)),
731
            this, SLOT(onAboutToUnloadSession()));
con's avatar
con committed
732 733 734 735 736 737

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

    // thread connections
    connect(this, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)),
            this, SLOT(onDocumentUpdated(CPlusPlus::Document::Ptr)));
738 739
    connect(this, SIGNAL(extraDiagnosticsUpdated(QString)),
            this, SLOT(onExtraDiagnosticsUpdated(QString)));
con's avatar
con committed
740 741

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

Robert Loehning's avatar
Robert Loehning committed
745 746
    connect(Core::ICore::editorManager(), SIGNAL(editorAboutToClose(Core::IEditor*)),
        this, SLOT(editorAboutToClose(Core::IEditor*)));
747

748 749
    m_completionFallback = new InternalCompletionAssistProvider;
    m_completionAssistProvider = m_completionFallback;
750
    ExtensionSystem::PluginManager::addObject(m_completionAssistProvider);
751 752
    m_highlightingFallback = new CppHighlightingSupportInternalFactory;
    m_highlightingFactory = m_highlightingFallback;
con's avatar
con committed
753 754 755
}

CppModelManager::~CppModelManager()
756
{
757
    ExtensionSystem::PluginManager::removeObject(m_completionAssistProvider);
758 759 760
    delete m_completionFallback;
    delete m_highlightingFallback;
}
con's avatar
con committed
761

762
Snapshot CppModelManager::snapshot() const
763
{
764
    QMutexLocker locker(&protectSnapshot);
765 766
    return m_snapshot;
}
con's avatar
con committed
767

768 769 770 771 772 773
void CppModelManager::ensureUpdated()
{
    QMutexLocker locker(&mutex);
    if (! m_dirty)
        return;

774 775 776 777
    m_projectFiles = internalProjectFiles();
    m_includePaths = internalIncludePaths();
    m_frameworkPaths = internalFrameworkPaths();
    m_definedMacros = internalDefinedMacros();
778 779 780
    m_dirty = false;
}

781
QStringList CppModelManager::internalProjectFiles() const
con's avatar
con committed
782 783 784 785 786 787
{
    QStringList files;
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects);
    while (it.hasNext()) {
        it.next();
        ProjectInfo pinfo = it.value();
788 789
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts())
            files += part->sourceFiles;
con's avatar
con committed
790
    }
791
    files.removeDuplicates();
con's avatar
con committed
792 793 794
    return files;
}

795
QStringList CppModelManager::internalIncludePaths() const
con's avatar
con committed
796 797 798 799 800 801
{
    QStringList includePaths;
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects);
    while (it.hasNext()) {
        it.next();
        ProjectInfo pinfo = it.value();
802 803
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts())
            includePaths += part->includePaths;
con's avatar
con committed
804
    }
805
    includePaths.removeDuplicates();
con's avatar
con committed
806 807 808
    return includePaths;
}

809
QStringList CppModelManager::internalFrameworkPaths() const
con's avatar
con committed
810 811 812 813 814 815
{
    QStringList frameworkPaths;
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects);
    while (it.hasNext()) {
        it.next();
        ProjectInfo pinfo = it.value();
816 817
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts())
            frameworkPaths += part->frameworkPaths;
con's avatar
con committed
818
    }
819
    frameworkPaths.removeDuplicates();
con's avatar
con committed
820 821 822
    return frameworkPaths;
}

823
QByteArray CppModelManager::internalDefinedMacros() const
con's avatar
con committed
824 825
{
    QByteArray macros;
826
    QSet<QByteArray> alreadyIn;
con's avatar
con committed
827 828 829 830
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects);
    while (it.hasNext()) {
        it.next();
        ProjectInfo pinfo = it.value();
831 832 833 834 835 836 837 838 839 840
        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
841 842 843 844
    }
    return macros;
}

845 846 847 848 849 850 851 852 853 854
void CppModelManager::addEditorSupport(AbstractEditorSupport *editorSupport)
{
    m_addtionalEditorSupport.insert(editorSupport);
}

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

855
QList<int> CppModelManager::references(CPlusPlus::Symbol *symbol, const LookupContext &context)
856
{
857
    return m_findReferences->references(symbol, context);
858 859
}

860
void CppModelManager::findUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context)
Roberto Raggi's avatar
Roberto Raggi committed
861
{
862
    if (symbol->identifier())
863
        m_findReferences->findUsages(symbol, context);
864 865
}

866 867
void CppModelManager::renameUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context,
                                   const QString &replacement)
868 869
{
    if (symbol->identifier())
870
        m_findReferences->renameUsages(symbol, context, replacement);
Roberto Raggi's avatar
Roberto Raggi committed
871 872
}

Christian Kamm's avatar
Christian Kamm committed
873 874 875 876 877
void CppModelManager::findMacroUsages(const CPlusPlus::Macro &macro)
{
    m_findReferences->findMacroUses(macro);
}

878 879 880 881 882
void CppModelManager::renameMacroUsages(const CPlusPlus::Macro &macro, const QString &replacement)
{
    m_findReferences->renameMacroUses(macro, replacement);
}

883
CppModelManager::WorkingCopy CppModelManager::buildWorkingCopyList()
con's avatar
con committed
884
{
885
    WorkingCopy workingCopy;
con's avatar
con committed
886 887 888 889 890
    QMapIterator<TextEditor::ITextEditor *, CppEditorSupport *> it(m_editorSupport);
    while (it.hasNext()) {
        it.next();
        TextEditor::ITextEditor *textEditor = it.key();
        CppEditorSupport *editorSupport = it.value();
891
        QString fileName = textEditor->document()->fileName();
892
        workingCopy.insert(fileName, editorSupport->contents(), editorSupport->editorRevision());
con's avatar
con committed
893 894
    }

895 896 897
    QSetIterator<AbstractEditorSupport *> jt(m_addtionalEditorSupport);
    while (jt.hasNext()) {
        AbstractEditorSupport *es =  jt.next();
898
        workingCopy.insert(es->fileName(), es->contents());
899 900
    }

con's avatar
con committed
901 902 903
    // add the project configuration file
    QByteArray conf(pp_configuration);
    conf += definedMacros();
904
    workingCopy.insert(pp_configuration_file, conf);
con's avatar
con committed
905 906 907 908

    return workingCopy;
}

909
CppModelManager::WorkingCopy CppModelManager::workingCopy() const
910 911 912 913
{
    return const_cast<CppModelManager *>(this)->buildWorkingCopyList();
}

dt's avatar
dt committed
914 915
QFuture<void> CppModelManager::updateSourceFiles(const QStringList &sourceFiles)
{ return refreshSourceFiles(sourceFiles); }
con's avatar
con committed
916

917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932
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)
{
933 934 935
#if 0
    // Tons of debug output...
    qDebug()<<"========= CppModelManager::updateProjectInfo ======";
Erik Verbruggen's avatar
Erik Verbruggen committed
936 937
    qDebug()<<" for project:"<< pinfo.project().data()->document()->fileName();
    foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
938 939
        qDebug() << "=== part ===";
        qDebug() << "language:" << (part->language == CXX ? "C++" : "ObjC++");
Erik Verbruggen's avatar
Erik Verbruggen committed
940 941
        qDebug() << "C++11:" << part->cxx11Enabled;
        qDebug() << "Qt version:" << part->qtVersion;
942 943 944 945 946 947 948 949 950 951
        qDebug() << "precompiled header:" << part->precompiledHeaders;
        qDebug() << "defines:" << part->defines;
        qDebug() << "includes:" << part->includePaths;
        qDebug() << "frameworkPaths:" << part->frameworkPaths;
        qDebug() << "sources:" << part->sourceFiles;
        qDebug() << "";
    }

    qDebug() << "";