cppmodelmanager.cpp 45.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 "cppindexingsupport.h"
38
#include "abstracteditorsupport.h"
39 40 41
#include "cpptoolsconstants.h"
#include "cpptoolseditorsupport.h"
#include "cppfindreferences.h"
con's avatar
con committed
42

43
#include <functional>
44
#include <QtConcurrentRun>
45 46 47 48 49 50 51 52 53 54 55 56 57
#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>
58

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

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

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

84
#include <QTextBlock>
85

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

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

    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;
109
    if (p1.cxx11Enabled != p2.cxx11Enabled)
110 111 112 113 114 115 116
        return false;
    if (p1.includePaths != p2.includePaths)
        return false;
    return p1.frameworkPaths == p2.frameworkPaths;
}
} // namespace CPlusPlus

117 118
using namespace CppTools;
using namespace CppTools::Internal;
con's avatar
con committed
119 120
using namespace CPlusPlus;

121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
#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
161
#endif // QTCREATOR_WITH_DUMP_AST
162

con's avatar
con committed
163 164 165 166 167 168 169 170 171 172
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"
173
    "#define __restrict__\n"
con's avatar
con committed
174

175 176 177 178
    "#define __complex__\n"
    "#define __imag__\n"
    "#define __real__\n"

179 180
    "#define __builtin_va_arg(a,b) ((b)0)\n"

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

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

CppPreprocessor::~CppPreprocessor()
207
{ }
con's avatar
con committed
208

209 210 211
void CppPreprocessor::setRevision(unsigned revision)
{ m_revision = revision; }

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

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

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

222 223 224 225 226 227 228 229 230 231 232 233
        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;
                }
234
            }
235 236 237
            m_includePaths.append(path);
        } else {
            m_includePaths.append(path);
238 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 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
{
    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
278

279 280 281
void CppPreprocessor::setProjectFiles(const QStringList &files)
{ m_projectFiles = files; }

282 283 284
void CppPreprocessor::setTodo(const QStringList &files)
{ m_todo = QSet<QString>::fromList(files); }

Wolfgang Beck's avatar
Wolfgang Beck committed
285
namespace {
286
class Process: public std::unary_function<Document::Ptr, void>
287 288
{
    QPointer<CppModelManager> _modelManager;
289 290
    Snapshot _snapshot;
    Document::Ptr _doc;
Roberto Raggi's avatar
Roberto Raggi committed
291
    Document::CheckMode _mode;
292 293

public:
294
    Process(QPointer<CppModelManager> modelManager,
Roberto Raggi's avatar
Roberto Raggi committed
295 296
            Document::Ptr doc,
            const Snapshot &snapshot,
297
            const CppModelManager::WorkingCopy &workingCopy)
298 299
        : _modelManager(modelManager),
          _snapshot(snapshot),
Roberto Raggi's avatar
Roberto Raggi committed
300 301
          _doc(doc),
          _mode(Document::FastCheck)
302
    {
303

Roberto Raggi's avatar
Roberto Raggi committed
304 305 306
        if (workingCopy.contains(_doc->fileName()))
            _mode = Document::FullCheck;
    }
307

Roberto Raggi's avatar
Roberto Raggi committed
308 309 310
    void operator()()
    {
        _doc->check(_mode);
311

312
        if (_modelManager)
313
            _modelManager->emitDocumentUpdated(_doc);
314 315

        _doc->releaseSourceAndAST();
316 317 318 319 320 321 322
    }
};
} // end of anonymous namespace

void CppPreprocessor::run(const QString &fileName)
{
    QString absoluteFilePath = fileName;
323
    sourceNeeded(0, absoluteFilePath, IncludeGlobal);
324
}
325

326
void CppPreprocessor::resetEnvironment()
327 328 329 330
{
    env.reset();
    m_processed.clear();
}
331

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

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

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

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

361 362 363
    return false;
}

364
QString CppPreprocessor::tryIncludeFile(QString &fileName, IncludeType type, unsigned *revision)
Roberto Raggi's avatar
Roberto Raggi committed
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385
{
    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;
}

386 387 388 389 390 391
static inline void appendDirSeparatorIfNeeded(QString &path)
{
    if (!path.endsWith(QLatin1Char('/'), Qt::CaseInsensitive))
        path += QLatin1Char('/');
}

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

    if (type == IncludeLocal && m_currentDoc) {
        QFileInfo currentFileInfo(m_currentDoc->fileName());
        QString path = currentFileInfo.absolutePath();
404
        appendDirSeparatorIfNeeded(path);
405 406
        path += fileName;
        path = QDir::cleanPath(path);
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 415
    foreach (const QString &includePath, m_includePaths) {
        QString path = includePath;
416
        appendDirSeparatorIfNeeded(path);
417 418
        path += fileName;
        path = QDir::cleanPath(path);
419
        QString contents;
420
        if (includeFile(path, &contents, revision)) {
421 422
            fileName = path;
            return contents;
con's avatar
con committed
423
        }
424
    }
con's avatar
con committed
425

426 427 428
    // look in the system include paths
    foreach (const QString &includePath, m_systemIncludePaths) {
        QString path = includePath;
429
        appendDirSeparatorIfNeeded(path);
430 431
        path += fileName;
        path = QDir::cleanPath(path);
432
        QString contents;
433
        if (includeFile(path, &contents, revision)) {
434 435
            fileName = path;
            return contents;
con's avatar
con committed
436
        }
437 438 439 440 441 442
    }

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

444 445
        foreach (const QString &frameworkPath, m_frameworkPaths) {
            QString path = frameworkPath;
446
            appendDirSeparatorIfNeeded(path);
447 448 449
            path += frameworkName;
            path += QLatin1String(".framework/Headers/");
            path += name;
450
            path = QDir::cleanPath(path);
451
            QString contents;
452
            if (includeFile(path, &contents, revision)) {
con's avatar
con committed
453 454 455 456
                fileName = path;
                return contents;
            }
        }
457
    }
con's avatar
con committed
458

459 460 461
    QString path = fileName;
    if (path.at(0) != QLatin1Char('/'))
        path.prepend(QLatin1Char('/'));
con's avatar
con committed
462

463 464 465
    foreach (const QString &projectFile, m_projectFiles) {
        if (projectFile.endsWith(path)) {
            fileName = projectFile;
466
            QString contents;
467
            includeFile(fileName, &contents, revision);
468
            return contents;
con's avatar
con committed
469 470 471
        }
    }

472
    //qDebug() << "**** file" << fileName << "not found!";
473
    return QString();
474
}
con's avatar
con committed
475

Roberto Raggi's avatar
Roberto Raggi committed
476
void CppPreprocessor::macroAdded(const Macro &macro)
477 478 479
{
    if (! m_currentDoc)
        return;
con's avatar
con committed
480

Roberto Raggi's avatar
Roberto Raggi committed
481
    m_currentDoc->appendMacro(macro);
482
}
483

484 485 486 487 488 489 490
static inline const Macro revision(const CppModelManagerInterface::WorkingCopy &s, const Macro &macro)
{
    Macro newMacro(macro);
    newMacro.setFileRevision(s.get(macro.fileName()).second);
    return newMacro;
}

491
void CppPreprocessor::passedMacroDefinitionCheck(unsigned offset, unsigned line, const Macro &macro)
Christian Kamm's avatar
Christian Kamm committed
492 493 494 495
{
    if (! m_currentDoc)
        return;

496
    m_currentDoc->addMacroUse(revision(m_workingCopy, macro), offset, macro.name().length(), line,
497
                              QVector<MacroArgumentReference>());
Christian Kamm's avatar
Christian Kamm committed
498 499
}

500
void CppPreprocessor::failedMacroDefinitionCheck(unsigned offset, const ByteArrayRef &name)
Christian Kamm's avatar
Christian Kamm committed
501 502 503 504
{
    if (! m_currentDoc)
        return;

505
    m_currentDoc->addUndefinedMacroUse(QByteArray(name.start(), name.size()), offset);
Christian Kamm's avatar
Christian Kamm committed
506 507
}

508 509 510 511 512
void CppPreprocessor::notifyMacroReference(unsigned offset, unsigned line, const Macro &macro)
{
    if (! m_currentDoc)
        return;

513
    m_currentDoc->addMacroUse(revision(m_workingCopy, macro), offset, macro.name().length(), line,
514 515 516 517
                              QVector<MacroArgumentReference>());
}

void CppPreprocessor::startExpandingMacro(unsigned offset, unsigned line,
Roberto Raggi's avatar
Roberto Raggi committed
518
                                          const Macro &macro,
519
                                          const QVector<MacroArgumentReference> &actuals)
520 521 522
{
    if (! m_currentDoc)
        return;
523

524
    m_currentDoc->addMacroUse(revision(m_workingCopy, macro), offset, macro.name().length(), line, actuals);
525
}
526

527
void CppPreprocessor::stopExpandingMacro(unsigned, const Macro &)
528 529 530
{
    if (! m_currentDoc)
        return;
con's avatar
con committed
531

532 533
    //qDebug() << "stop expanding:" << macro.name;
}
con's avatar
con committed
534

535 536 537 538
void CppPreprocessor::mergeEnvironment(Document::Ptr doc)
{
    if (! doc)
        return;
con's avatar
con committed
539

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

542
    if (m_processed.contains(fn))
543
        return;
con's avatar
con committed
544

545
    m_processed.insert(fn);
con's avatar
con committed
546

547
    foreach (const Document::Include &incl, doc->includes()) {
548 549
        QString includedFile = incl.fileName();

550
        if (Document::Ptr includedDoc = snapshot.document(includedFile))
551
            mergeEnvironment(includedDoc);
552 553
        else
            run(includedFile);
Roberto Raggi's avatar
Roberto Raggi committed
554
    }
con's avatar
con committed
555

556
    env.addMacros(doc->definedMacros());
557
}
con's avatar
con committed
558

559 560 561 562 563 564 565 566 567 568 569 570 571 572
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);
}

573
void CppPreprocessor::sourceNeeded(unsigned line, QString &fileName, IncludeType type)
574 575 576 577
{
    if (fileName.isEmpty())
        return;

578 579
    unsigned editorRevision = 0;
    QString contents = tryIncludeFile(fileName, type, &editorRevision);
580
    fileName = QDir::cleanPath(fileName);
581
    if (m_currentDoc) {
582
        m_currentDoc->addIncludeFile(fileName, line);
583

584
        if (contents.isEmpty() && ! QFileInfo(fileName).isAbsolute()) {
585 586
            QString msg = QCoreApplication::translate(
                    "CppPreprocessor", "%1: No such file or directory").arg(fileName);
587

588 589
            Document::DiagnosticMessage d(Document::DiagnosticMessage::Warning,
                                          m_currentDoc->fileName(),
590
                                          line, /*column = */ 0,
591
                                          msg);
592

593
            m_currentDoc->addDiagnosticMessage(d);
594

595
            //qWarning() << "file not found:" << fileName << m_currentDoc->fileName() << env.current_line;
con's avatar
con committed
596
        }
597
    }
con's avatar
con committed
598

599 600
    if (m_dumpFileNameWhileParsing) {
        qDebug() << "Parsing file:" << fileName
601
//             << "contents:" << contents.size()
602 603
                    ;
    }
con's avatar
con committed
604

605
    Document::Ptr doc = snapshot.document(fileName);
606 607 608 609
    if (doc) {
        mergeEnvironment(doc);
        return;
    }
con's avatar
con committed
610

611
    doc = Document::create(fileName);
612
    doc->setRevision(m_revision);
613
    doc->setEditorRevision(editorRevision);
con's avatar
con committed
614

615 616 617 618
    QFileInfo info(fileName);
    if (info.exists())
        doc->setLastModified(info.lastModified());

619
    Document::Ptr previousDoc = switchDocument(doc);
con's avatar
con committed
620

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

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

625
    doc->setUtf8Source(preprocessedCode);
626
    doc->keepSourceAndAST();
627
    doc->tokenize();
628

629
    snapshot.insert(doc);
630
    m_todo.remove(fileName);
con's avatar
con committed
631

Roberto Raggi's avatar
Roberto Raggi committed
632
    Process process(m_modelManager, doc, snapshot, m_workingCopy);
633

Roberto Raggi's avatar
Roberto Raggi committed
634
    process();
635 636

    (void) switchDocument(previousDoc);
637
}
con's avatar
con committed
638

639 640 641 642 643 644
Document::Ptr CppPreprocessor::switchDocument(Document::Ptr doc)
{
    Document::Ptr previousDoc = m_currentDoc;
    m_currentDoc = doc;
    return previousDoc;
}
con's avatar
con committed
645

646
void CppModelManager::updateModifiedSourceFiles()
647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664
{
    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);
}

665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802
namespace {

class IndexingSupport: public CppIndexingSupport {
public:
    typedef CppModelManagerInterface::WorkingCopy WorkingCopy;

public:
    IndexingSupport()
        : m_revision(0)
    {
        m_synchronizer.setCancelOnWait(true);
        m_dumpFileNameWhileParsing = !qgetenv("QTCREATOR_DUMP_FILENAME_WHILE_PARSING").isNull();
    }

    ~IndexingSupport()
    {}

    QFuture<void> refreshSourceFiles(const QStringList &sourceFiles)
    {
        CppModelManager *mgr = CppModelManager::instance();
        const WorkingCopy workingCopy = mgr->workingCopy();

        CppPreprocessor *preproc = new CppPreprocessor(mgr, m_dumpFileNameWhileParsing);
        preproc->setRevision(++m_revision);
        preproc->setProjectFiles(mgr->projectFiles());
        preproc->setIncludePaths(mgr->includePaths());
        preproc->setFrameworkPaths(mgr->frameworkPaths());
        preproc->setWorkingCopy(workingCopy);

        QFuture<void> result = QtConcurrent::run(&parse, preproc, sourceFiles);

        if (m_synchronizer.futures().size() > 10) {
            QList<QFuture<void> > futures = m_synchronizer.futures();

            m_synchronizer.clearFutures();

            foreach (const QFuture<void> &future, futures) {
                if (! (future.isFinished() || future.isCanceled()))
                    m_synchronizer.addFuture(future);
            }
        }

        m_synchronizer.addFuture(result);

        if (sourceFiles.count() > 1) {
            Core::ICore::progressManager()->addTask(result,
                                                    QCoreApplication::translate("IndexingSupport", "Parsing"),
                                                    CppTools::Constants::TASK_INDEX);
        }

        return result;
    }

private:
    static void parse(QFutureInterface<void> &future,
                      CppPreprocessor *preproc,
                      QStringList files)
    {
        if (files.isEmpty())
            return;

        const Core::MimeDatabase *mimeDb = Core::ICore::mimeDatabase();
        Core::MimeType cSourceTy = mimeDb->findByType(QLatin1String("text/x-csrc"));
        Core::MimeType cppSourceTy = mimeDb->findByType(QLatin1String("text/x-c++src"));
        Core::MimeType mSourceTy = mimeDb->findByType(QLatin1String("text/x-objcsrc"));

        QStringList sources;
        QStringList headers;

        QStringList suffixes = cSourceTy.suffixes();
        suffixes += cppSourceTy.suffixes();
        suffixes += mSourceTy.suffixes();

        foreach (const QString &file, files) {
            QFileInfo info(file);

            preproc->snapshot.remove(file);

            if (suffixes.contains(info.suffix()))
                sources.append(file);
            else
                headers.append(file);
        }

        const int sourceCount = sources.size();
        files = sources;
        files += headers;

        preproc->setTodo(files);

        future.setProgressRange(0, files.size());

        QString conf = QLatin1String(pp_configuration_file);

        bool processingHeaders = false;

        for (int i = 0; i < files.size(); ++i) {
            if (future.isPaused())
                future.waitForResume();

            if (future.isCanceled())
                break;

            const QString fileName = files.at(i);

            const bool isSourceFile = i < sourceCount;
            if (isSourceFile)
                (void) preproc->run(conf);

            else if (! processingHeaders) {
                (void) preproc->run(conf);

                processingHeaders = true;
            }

            preproc->run(fileName);

            future.setProgressValue(files.size() - preproc->todo().size());

            if (isSourceFile)
                preproc->resetEnvironment();
        }

        future.setProgressValue(files.size());
        preproc->modelManager()->finishedRefreshingSourceFiles(files);

        delete preproc;
    }

private:
    QFutureSynchronizer<void> m_synchronizer;
    unsigned m_revision;
    bool m_dumpFileNameWhileParsing;
};


} // anonymous namespace

con's avatar
con committed
803 804 805 806 807 808
/*!
    \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
809
    modified within Qt Creator.
con's avatar
con committed
810 811
*/

812 813 814 815 816 817 818 819 820 821 822 823 824 825
QMutex CppModelManager::m_modelManagerMutex;
CppModelManager *CppModelManager::m_modelManagerInstance = 0;

CppModelManager *CppModelManager::instance()
{
    if (m_modelManagerInstance)
        return m_modelManagerInstance;
    QMutexLocker locker(&m_modelManagerMutex);
    if (!m_modelManagerInstance) {
        m_modelManagerInstance = new CppModelManager;
    }
    return m_modelManagerInstance;
}

826
CppModelManager::CppModelManager(QObject *parent)
827
    : CppModelManagerInterface(parent)
con's avatar
con committed
828
{
Roberto Raggi's avatar
Roberto Raggi committed
829
    m_findReferences = new CppFindReferences(this);
830
    m_indexerEnabled = qgetenv("QTCREATOR_NO_CODE_INDEXER").isNull();
Roberto Raggi's avatar
Roberto Raggi committed
831

832 833
    m_dirty = true;

834
    ProjectExplorer::ProjectExplorerPlugin *pe =
835
       ProjectExplorer::ProjectExplorerPlugin::instance();
con's avatar
con committed
836

837
    QTC_ASSERT(pe, return);
con's avatar
con committed
838

839
    ProjectExplorer::SessionManager *session = pe->session();
Roberto Raggi's avatar
Roberto Raggi committed
840 841 842 843 844 845
    m_updateEditorSelectionsTimer = new QTimer(this);
    m_updateEditorSelectionsTimer->setInterval(500);
    m_updateEditorSelectionsTimer->setSingleShot(true);
    connect(m_updateEditorSelectionsTimer, SIGNAL(timeout()),
            this, SLOT(updateEditorSelections()));

846 847 848
    connect(session, SIGNAL(projectAdded(ProjectExplorer::Project*)),
            this, SLOT(onProjectAdded(ProjectExplorer::Project*)));

Robert Loehning's avatar
Robert Loehning committed
849 850
    connect(session, SIGNAL(aboutToRemoveProject(ProjectExplorer::Project*)),
            this, SLOT(onAboutToRemoveProject(ProjectExplorer::Project*)));
con's avatar
con committed
851

852
    connect(session, SIGNAL(aboutToUnloadSession(QString)),
853
            this, SLOT(onAboutToUnloadSession()));
con's avatar
con committed
854 855 856 857 858 859

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

    // thread connections
    connect(this, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)),
            this, SLOT(onDocumentUpdated(CPlusPlus::Document::Ptr)));
860 861
    connect(this, SIGNAL(extraDiagnosticsUpdated(QString)),
            this, SLOT(onExtraDiagnosticsUpdated(QString)));
con's avatar
con committed
862 863

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

Robert Loehning's avatar
Robert Loehning committed
867 868
    connect(Core::ICore::editorManager(), SIGNAL(editorAboutToClose(Core::IEditor*)),
        this, SLOT(editorAboutToClose(Core::IEditor*)));
869

870 871
    m_completionFallback = new InternalCompletionAssistProvider;
    m_completionAssistProvider = m_completionFallback;
872
    ExtensionSystem::PluginManager::addObject(m_completionAssistProvider);
873 874
    m_highlightingFallback = new CppHighlightingSupportInternalFactory;
    m_highlightingFactory = m_highlightingFallback;
875
    m_internalIndexingSupport = new IndexingSupport;
con's avatar
con committed
876 877 878
}

CppModelManager::~CppModelManager()
879
{
880
    ExtensionSystem::PluginManager::removeObject(m_completionAssistProvider);
881 882
    delete m_completionFallback;
    delete m_highlightingFallback;
883
    delete m_internalIndexingSupport;
884
}
con's avatar
con committed
885

886
Snapshot CppModelManager::snapshot() const
887
{
888
    QMutexLocker locker(&protectSnapshot);
889 890
    return m_snapshot;
}
con's avatar
con committed
891

892 893 894 895 896 897
void CppModelManager::ensureUpdated()
{
    QMutexLocker locker(&mutex);
    if (! m_dirty)
        return;

898 899 900 901
    m_projectFiles = internalProjectFiles();
    m_includePaths = internalIncludePaths();
    m_frameworkPaths = internalFrameworkPaths();
    m_definedMacros = internalDefinedMacros();
902 903 904
    m_dirty = false;
}

905
QStringList CppModelManager::internalProjectFiles() const
con's avatar
con committed
906 907 908 909 910 911
{
    QStringList files;
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects);
    while (it.hasNext()) {
        it.next();
        ProjectInfo pinfo = it.value();
912 913
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts())
            files += part->sourceFiles;
con's avatar
con committed
914
    }
915
    files.removeDuplicates();
con's avatar
con committed
916 917 918
    return files;
}

919
QStringList CppModelManager::internalIncludePaths() const
con's avatar
con committed
920 921 922 923 924 925
{
    QStringList includePaths;
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects);
    while (it.hasNext()) {
        it.next();
        ProjectInfo pinfo = it.value();
926 927
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts())
            includePaths += part->includePaths;
con's avatar
con committed
928
    }
929
    includePaths.removeDuplicates();
con's avatar
con committed
930 931 932
    return includePaths;
}

933
QStringList CppModelManager::internalFrameworkPaths() const
con's avatar
con committed
934 935 936 937 938 939
{
    QStringList frameworkPaths;
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects);
    while (it.hasNext()) {
        it.next();
        ProjectInfo pinfo = it.value();
940 941
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts())
            frameworkPaths += part->frameworkPaths;
con's avatar
con committed
942
    }
943
    frameworkPaths.removeDuplicates();
con's avatar
con committed
944 945 946
    return frameworkPaths;
}

947
QByteArray CppModelManager::internalDefinedMacros() const
con's avatar
con committed
948 949
{
    QByteArray macros;
950
    QSet<QByteArray> alreadyIn;
con's avatar
con committed
951 952 953 954
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects);
    while (it.hasNext()) {
        it.next();
        ProjectInfo pinfo = it.value();
955 956 957 958 959 960 961 962 963 964
        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
965 966 967 968
    }
    return macros;
}

969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001
/// This method will aquire the mutex!
void CppModelManager::dumpModelManagerConfiguration()
{
    // Tons of debug output...
    qDebug()<<"========= CppModelManager::dumpModelManagerConfiguration ======";
    foreach (const ProjectInfo &pinfo, m_projects.values()) {
        qDebug()<<" for project:"<< pinfo.project().data()->document()->fileName();
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
            qDebug() << "=== part ===";
            qDebug() << "language:" << (part->language == CXX ? "C++" : "ObjC++");
            qDebug() << "C++11:" << part->cxx11Enabled;
            qDebug() << "Qt version:" << part->qtVersion;
            qDebug() << "precompiled header:" << part->precompiledHeaders;
            qDebug() << "defines:" << part->defines;
            qDebug() << "includes:" << part->includePaths;
            qDebug() << "frameworkPaths:" << part->frameworkPaths;
            qDebug() << "sources:" << part->sourceFiles;
            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 ======";
}

1002 1003 1004 1005 1006 1007 1008 1009 1010 1011
void CppModelManager::addEditorSupport(AbstractEditorSupport *editorSupport)
{
    m_addtionalEditorSupport.insert(editorSupport);
}

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

1012
QList<int> CppModelManager::references(CPlusPlus::Symbol *symbol, const LookupContext &context)
1013
{
1014
    return m_findReferences->references(symbol, context);
1015 1016
}

1017
void CppModelManager::findUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context)
Roberto Raggi's avatar
Roberto Raggi committed
1018
{
1019
    if (symbol->identifier())
1020
        m_findReferences->findUsages(symbol, context);
1021 1022
}

1023 1024
void CppModelManager::renameUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context,
                                   const QString &replacement)
1025 1026
{
    if (symbol->identifier())
1027
        m_findReferences->renameUsages(symbol, context, replacement);
Roberto Raggi's avatar
Roberto Raggi committed
1028 1029
}

Christian Kamm's avatar
Christian Kamm committed
1030 1031 1032 1033 1034
void CppModelManager::findMacroUsages(const CPlusPlus::Macro &macro)
{
    m_findReferences->findMacroUses(macro);
}