cppmodelmanager.cpp 42 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 "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 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
CppPreprocessor::CppPreprocessor(QPointer<CppModelManager> modelManager, bool dumpFileNameWhileParsing)
201 202
    : snapshot(modelManager->snapshot()),
      m_modelManager(modelManager),
203
      m_dumpFileNameWhileParsing(dumpFileNameWhileParsing),
204 205
      preprocess(this, &env),
      m_revision(0)
206 207 208
{
    preprocess.setKeepComments(true);
}
Roberto Raggi's avatar
Roberto Raggi committed
209 210

CppPreprocessor::~CppPreprocessor()
211
{ }
con's avatar
con committed
212

213 214 215
void CppPreprocessor::setRevision(unsigned revision)
{ m_revision = revision; }

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

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

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

226 227 228 229 230 231 232 233 234 235 236 237
        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;
                }
238
            }
239 240 241
            m_includePaths.append(path);
        } else {
            m_includePaths.append(path);
242 243 244
        }
    }
}
245 246

void CppPreprocessor::setFrameworkPaths(const QStringList &frameworkPaths)
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 278 279 280 281
{
    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
282

283 284 285
void CppPreprocessor::setProjectFiles(const QStringList &files)
{ m_projectFiles = files; }

286 287 288
void CppPreprocessor::setTodo(const QStringList &files)
{ m_todo = QSet<QString>::fromList(files); }

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

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

Roberto Raggi's avatar
Roberto Raggi committed
308 309 310
        if (workingCopy.contains(_doc->fileName()))
            _mode = Document::FullCheck;
    }
311

Roberto Raggi's avatar
Roberto Raggi committed
312 313 314
    void operator()()
    {
        _doc->check(_mode);
315

316
        if (_modelManager)
317
            _modelManager->emitDocumentUpdated(_doc);
318 319

        _doc->releaseSourceAndAST();
320 321 322 323 324 325 326
    }
};
} // end of anonymous namespace

void CppPreprocessor::run(const QString &fileName)
{
    QString absoluteFilePath = fileName;
327
    sourceNeeded(0, absoluteFilePath, IncludeGlobal);
328
}
329

330
void CppPreprocessor::resetEnvironment()
331 332 333 334
{
    env.reset();
    m_processed.clear();
}
335

336
bool CppPreprocessor::includeFile(const QString &absoluteFilePath, QString *result, unsigned *revision)
337
{
338
    if (absoluteFilePath.isEmpty() || m_included.contains(absoluteFilePath))
339 340 341 342
        return true;

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

    QFileInfo fileInfo(absoluteFilePath);
    if (! fileInfo.isFile())
con's avatar
con committed
351
        return false;
352 353

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

365 366 367
    return false;
}

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

390 391 392 393 394 395
static inline void appendDirSeparatorIfNeeded(QString &path)
{
    if (!path.endsWith(QLatin1Char('/'), Qt::CaseInsensitive))
        path += QLatin1Char('/');
}

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

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

418 419
    foreach (const QString &includePath, m_includePaths) {
        QString path = includePath;
420
        appendDirSeparatorIfNeeded(path);
421 422
        path += fileName;
        path = QDir::cleanPath(path);
423
        QString contents;
424
        if (includeFile(path, &contents, revision)) {
425 426
            fileName = path;
            return contents;
con's avatar
con committed
427
        }
428
    }
con's avatar
con committed
429

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

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

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

463 464 465
    QString path = fileName;
    if (path.at(0) != QLatin1Char('/'))
        path.prepend(QLatin1Char('/'));
con's avatar
con committed
466

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

476
    //qDebug() << "**** file" << fileName << "not found!";
477
    return QString();
478
}
con's avatar
con committed
479

Roberto Raggi's avatar
Roberto Raggi committed
480
void CppPreprocessor::macroAdded(const Macro &macro)
481 482 483
{
    if (! m_currentDoc)
        return;
con's avatar
con committed
484

Roberto Raggi's avatar
Roberto Raggi committed
485
    m_currentDoc->appendMacro(macro);
486
}
487

488 489 490 491 492 493 494
static inline const Macro revision(const CppModelManagerInterface::WorkingCopy &s, const Macro &macro)
{
    Macro newMacro(macro);
    newMacro.setFileRevision(s.get(macro.fileName()).second);
    return newMacro;
}

495
void CppPreprocessor::passedMacroDefinitionCheck(unsigned offset, unsigned line, const Macro &macro)
Christian Kamm's avatar
Christian Kamm committed
496 497 498 499
{
    if (! m_currentDoc)
        return;

500
    m_currentDoc->addMacroUse(revision(m_workingCopy, macro), offset, macro.name().length(), line,
501
                              QVector<MacroArgumentReference>());
Christian Kamm's avatar
Christian Kamm committed
502 503
}

504
void CppPreprocessor::failedMacroDefinitionCheck(unsigned offset, const ByteArrayRef &name)
Christian Kamm's avatar
Christian Kamm committed
505 506 507 508
{
    if (! m_currentDoc)
        return;

509
    m_currentDoc->addUndefinedMacroUse(QByteArray(name.start(), name.size()), offset);
Christian Kamm's avatar
Christian Kamm committed
510 511
}

512 513 514 515 516
void CppPreprocessor::notifyMacroReference(unsigned offset, unsigned line, const Macro &macro)
{
    if (! m_currentDoc)
        return;

517
    m_currentDoc->addMacroUse(revision(m_workingCopy, macro), offset, macro.name().length(), line,
518 519 520 521
                              QVector<MacroArgumentReference>());
}

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

528
    m_currentDoc->addMacroUse(revision(m_workingCopy, macro), offset, macro.name().length(), line, actuals);
529
}
530

531
void CppPreprocessor::stopExpandingMacro(unsigned, const Macro &)
532 533 534
{
    if (! m_currentDoc)
        return;
con's avatar
con committed
535

536 537
    //qDebug() << "stop expanding:" << macro.name;
}
con's avatar
con committed
538

539 540 541 542
void CppPreprocessor::mergeEnvironment(Document::Ptr doc)
{
    if (! doc)
        return;
con's avatar
con committed
543

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

546
    if (m_processed.contains(fn))
547
        return;
con's avatar
con committed
548

549
    m_processed.insert(fn);
con's avatar
con committed
550

551
    foreach (const Document::Include &incl, doc->includes()) {
552 553
        QString includedFile = incl.fileName();

554
        if (Document::Ptr includedDoc = snapshot.document(includedFile))
555
            mergeEnvironment(includedDoc);
556 557
        else
            run(includedFile);
Roberto Raggi's avatar
Roberto Raggi committed
558
    }
con's avatar
con committed
559

560
    env.addMacros(doc->definedMacros());
561
}
con's avatar
con committed
562

563 564 565 566 567 568 569 570 571 572 573 574 575 576
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);
}

577
void CppPreprocessor::sourceNeeded(unsigned line, QString &fileName, IncludeType type)
578 579 580 581
{
    if (fileName.isEmpty())
        return;

582 583
    unsigned editorRevision = 0;
    QString contents = tryIncludeFile(fileName, type, &editorRevision);
584
    fileName = QDir::cleanPath(fileName);
585
    if (m_currentDoc) {
586
        m_currentDoc->addIncludeFile(fileName, line);
587

588
        if (contents.isEmpty() && ! QFileInfo(fileName).isAbsolute()) {
589 590
            QString msg = QCoreApplication::translate(
                    "CppPreprocessor", "%1: No such file or directory").arg(fileName);
591

592 593
            Document::DiagnosticMessage d(Document::DiagnosticMessage::Warning,
                                          m_currentDoc->fileName(),
594
                                          line, /*column = */ 0,
595
                                          msg);
596

597
            m_currentDoc->addDiagnosticMessage(d);
598

599
            //qWarning() << "file not found:" << fileName << m_currentDoc->fileName() << env.current_line;
con's avatar
con committed
600
        }
601
    }
con's avatar
con committed
602

603 604
    if (m_dumpFileNameWhileParsing) {
        qDebug() << "Parsing file:" << fileName
605
//             << "contents:" << contents.size()
606 607
                    ;
    }
con's avatar
con committed
608

609
    Document::Ptr doc = snapshot.document(fileName);
610 611 612 613
    if (doc) {
        mergeEnvironment(doc);
        return;
    }
con's avatar
con committed
614

615
    doc = Document::create(fileName);
616
    doc->setRevision(m_revision);
617
    doc->setEditorRevision(editorRevision);
con's avatar
con committed
618

619 620 621 622
    QFileInfo info(fileName);
    if (info.exists())
        doc->setLastModified(info.lastModified());

623
    Document::Ptr previousDoc = switchDocument(doc);
con's avatar
con committed
624

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

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

629
    doc->setUtf8Source(preprocessedCode);
630
    doc->keepSourceAndAST();
631
    doc->tokenize();
632

633
    snapshot.insert(doc);
634
    m_todo.remove(fileName);
con's avatar
con committed
635

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

Roberto Raggi's avatar
Roberto Raggi committed
638
    process();
639 640

    (void) switchDocument(previousDoc);
641
}
con's avatar
con committed
642

643 644 645 646 647 648
Document::Ptr CppPreprocessor::switchDocument(Document::Ptr doc)
{
    Document::Ptr previousDoc = m_currentDoc;
    m_currentDoc = doc;
    return previousDoc;
}
con's avatar
con committed
649

650
void CppModelManager::updateModifiedSourceFiles()
651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668
{
    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
669 670 671 672 673 674
/*!
    \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
675
    modified within Qt Creator.
con's avatar
con committed
676 677
*/

678 679 680 681 682 683 684 685 686 687 688 689 690 691
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;
}

692
CppModelManager::CppModelManager(QObject *parent)
693
    : CppModelManagerInterface(parent)
694 695 696
    , m_completionAssistProvider(0)
    , m_highlightingFactory(0)
    , m_indexingSupporter(0)
con's avatar
con committed
697
{
Roberto Raggi's avatar
Roberto Raggi committed
698
    m_findReferences = new CppFindReferences(this);
699
    m_indexerEnabled = qgetenv("QTCREATOR_NO_CODE_INDEXER").isNull();
Roberto Raggi's avatar
Roberto Raggi committed
700

701 702
    m_dirty = true;

703
    ProjectExplorer::ProjectExplorerPlugin *pe =
704
       ProjectExplorer::ProjectExplorerPlugin::instance();
con's avatar
con committed
705

706
    QTC_ASSERT(pe, return);
con's avatar
con committed
707

708
    ProjectExplorer::SessionManager *session = pe->session();
Roberto Raggi's avatar
Roberto Raggi committed
709 710 711 712 713 714
    m_updateEditorSelectionsTimer = new QTimer(this);
    m_updateEditorSelectionsTimer->setInterval(500);
    m_updateEditorSelectionsTimer->setSingleShot(true);
    connect(m_updateEditorSelectionsTimer, SIGNAL(timeout()),
            this, SLOT(updateEditorSelections()));

715 716 717
    connect(session, SIGNAL(projectAdded(ProjectExplorer::Project*)),
            this, SLOT(onProjectAdded(ProjectExplorer::Project*)));

Robert Loehning's avatar
Robert Loehning committed
718 719
    connect(session, SIGNAL(aboutToRemoveProject(ProjectExplorer::Project*)),
            this, SLOT(onAboutToRemoveProject(ProjectExplorer::Project*)));
con's avatar
con committed
720

721
    connect(session, SIGNAL(aboutToUnloadSession(QString)),
722
            this, SLOT(onAboutToUnloadSession()));
con's avatar
con committed
723 724 725 726 727 728

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

    // thread connections
    connect(this, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)),
            this, SLOT(onDocumentUpdated(CPlusPlus::Document::Ptr)));
729 730
    connect(this, SIGNAL(extraDiagnosticsUpdated(QString)),
            this, SLOT(onExtraDiagnosticsUpdated(QString)));
con's avatar
con committed
731 732

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

Robert Loehning's avatar
Robert Loehning committed
736 737
    connect(Core::ICore::editorManager(), SIGNAL(editorAboutToClose(Core::IEditor*)),
        this, SLOT(editorAboutToClose(Core::IEditor*)));
738

739 740
    m_completionFallback = new InternalCompletionAssistProvider;
    m_completionAssistProvider = m_completionFallback;
741
    ExtensionSystem::PluginManager::addObject(m_completionAssistProvider);
742 743
    m_highlightingFallback = new CppHighlightingSupportInternalFactory;
    m_highlightingFactory = m_highlightingFallback;
744
    m_internalIndexingSupport = new BuiltinIndexingSupport(pp_configuration_file);
con's avatar
con committed
745 746 747
}

CppModelManager::~CppModelManager()
748
{
749
    ExtensionSystem::PluginManager::removeObject(m_completionAssistProvider);
750 751
    delete m_completionFallback;
    delete m_highlightingFallback;
752
    delete m_internalIndexingSupport;
753
}
con's avatar
con committed
754

755
Snapshot CppModelManager::snapshot() const
756
{
757
    QMutexLocker locker(&protectSnapshot);
758 759
    return m_snapshot;
}
con's avatar
con committed
760

761 762 763 764 765 766
void CppModelManager::ensureUpdated()
{
    QMutexLocker locker(&mutex);
    if (! m_dirty)
        return;

767 768 769 770
    m_projectFiles = internalProjectFiles();
    m_includePaths = internalIncludePaths();
    m_frameworkPaths = internalFrameworkPaths();
    m_definedMacros = internalDefinedMacros();
771 772 773
    m_dirty = false;
}

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

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

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

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

841 842 843 844 845 846 847 848 849
/// 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 ===";
850 851 852 853 854 855 856 857 858 859
            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;
860 861 862 863 864
            qDebug() << "Qt version:" << part->qtVersion;
            qDebug() << "precompiled header:" << part->precompiledHeaders;
            qDebug() << "defines:" << part->defines;
            qDebug() << "includes:" << part->includePaths;
            qDebug() << "frameworkPaths:" << part->frameworkPaths;
865
            qDebug() << "headers:" << part->headerFiles;
866
            qDebug() << "sources:" << part->sourceFiles;
867
            qDebug() << "objc sources:" << part->objcSourceFiles;
868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883
            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 ======";
}

884 885 886 887 888 889 890 891 892 893
void CppModelManager::addEditorSupport(AbstractEditorSupport *editorSupport)
{
    m_addtionalEditorSupport.insert(editorSupport);
}

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

894
QList<int> CppModelManager::references(CPlusPlus::Symbol *symbol, const LookupContext &context)
895
{
896
    return m_findReferences->references(symbol, context);
897 898
}

899
void CppModelManager::findUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context)
Roberto Raggi's avatar
Roberto Raggi committed
900
{
901
    if (symbol->identifier())
902
        m_findReferences->findUsages(symbol, context);
903 904
}

905 906
void CppModelManager::renameUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context,
                                   const QString &replacement)
907 908
{
    if (symbol->identifier())
909
        m_findReferences->renameUsages(symbol, context, replacement);
Roberto Raggi's avatar
Roberto Raggi committed
910 911
}

Christian Kamm's avatar
Christian Kamm committed
912 913 914 915 916
void CppModelManager::findMacroUsages(const CPlusPlus::Macro &macro)
{
    m_findReferences->findMacroUses(macro);
}

917 918 919 920 921
void CppModelManager::renameMacroUsages(const CPlusPlus::Macro &macro, const QString &replacement)
{
    m_findReferences->renameMacroUses(macro, replacement);
}

922
CppModelManager::WorkingCopy CppModelManager::buildWorkingCopyList()
con's avatar
con committed
923
{
924
    WorkingCopy workingCopy;
con's avatar
con committed
925 926 927 928 929
    QMapIterator<TextEditor::ITextEditor *, CppEditorSupport *> it(m_editorSupport);
    while (it.hasNext()) {
        it.next();
        TextEditor::ITextEditor *textEditor = it.key();
        CppEditorSupport *editorSupport = it.value();
930
        QString fileName = textEditor->document()->fileName();
931
        workingCopy.insert(fileName, editorSupport->contents(), editorSupport->editorRevision());
con's avatar
con committed
932 933
    }

934 935 936
    QSetIterator<AbstractEditorSupport *> jt(m_addtionalEditorSupport);
    while (jt.hasNext()) {
        AbstractEditorSupport *es =  jt.next();
937
        workingCopy.insert(es->fileName(), QString::fromUtf8(es->contents()));
938 939
    }

con's avatar
con committed
940 941 942
    // add the project configuration file
    QByteArray conf(pp_configuration);
    conf += definedMacros();
943
    workingCopy.insert(QLatin1String(pp_configuration_file), QString::fromUtf8(conf));
con's avatar
con committed
944 945 946 947

    return workingCopy;
}

948
CppModelManager::WorkingCopy CppModelManager::workingCopy() const
949 950 951 952
{
    return const_cast<CppModelManager *>(this)->buildWorkingCopyList();
}

dt's avatar
dt committed
953
QFuture<void> CppModelManager::updateSourceFiles(const QStringList &sourceFiles)
954 955 956 957
{
    if (sourceFiles.isEmpty() || !m_indexerEnabled)
        return QFuture<void>();

958 959
    if (m_indexingSupporter)
        m_indexingSupporter->refreshSourceFiles(sourceFiles);
960 961
    return m_internalIndexingSupport->refreshSourceFiles(sourceFiles);
}
con's avatar
con committed
962

963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978
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)
{
979 980
    { // only hold the mutex for a limited scope, so the dumping afterwards can aquire it without deadlocking.
        QMutexLocker locker(&mutex);