tools/icheck/parser/src/plugins/cpptools/cppmodelmanager.cpp
changeset 0 876b1a06bc25
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/icheck/parser/src/plugins/cpptools/cppmodelmanager.cpp	Wed Aug 25 15:49:42 2010 +0300
@@ -0,0 +1,1366 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Mobility Components.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <cplusplus/pp.h>
+#include <cplusplus/CppBindings.h>
+#include <cplusplus/Overview.h>
+#include <cplusplus/CheckUndefinedSymbols.h>
+
+#include "cppmodelmanager.h"
+#ifndef ICHECK_BUILD
+#  include "cpptoolsconstants.h"
+#  include "cpptoolseditorsupport.h"
+#  include "cppfindreferences.h"
+#endif
+
+#include <functional>
+#include <QtConcurrentRun>
+#ifndef ICHECK_BUILD
+#  include <QFutureSynchronizer>
+#  include <qtconcurrent/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/uniqueidmanager.h>
+#  include <coreplugin/mimedatabase.h>
+#  include <coreplugin/editormanager/editormanager.h>
+#  include <coreplugin/progressmanager/progressmanager.h>
+#  include <extensionsystem/pluginmanager.h>
+#else
+#  include <QDir>
+#endif
+
+#include <utils/qtcassert.h>
+
+#include <TranslationUnit.h>
+#include <Semantic.h>
+#include <AST.h>
+#include <Scope.h>
+#include <Literals.h>
+#include <Symbols.h>
+#include <Names.h>
+#include <NameVisitor.h>
+#include <TypeVisitor.h>
+#include <ASTVisitor.h>
+#include <Lexer.h>
+#include <Token.h>
+
+#include <cplusplus/LookupContext.h>
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QDebug>
+#include <QtCore/QMutexLocker>
+#include <QtCore/QTime>
+#include <QtCore/QTimer>
+#include <QtConcurrentMap>
+#include <iostream>
+#include <sstream>
+
+using namespace CppTools;
+using namespace CppTools::Internal;
+using namespace CPlusPlus;
+
+#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; }
+};
+
+#endif // QTCREATOR_WITH_DUMP_AST
+
+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"
+    "#define __restrict__\n"
+
+    "#define __complex__\n"
+    "#define __imag__\n"
+    "#define __real__\n"
+
+    "#define __builtin_va_arg(a,b) ((b)0)\n"
+
+    // ### add macros for win32
+    "#define __cdecl\n"
+    "#define __stdcall\n"
+    "#define QT_WA(x) x\n"
+    "#define API\n"
+    "#define WINAPI\n"
+    "#define CALLBACK\n"
+    "#define STDMETHODCALLTYPE\n"
+    "#define __RPC_FAR\n"
+    "#define APIENTRY\n"
+    "#define __declspec(a)\n"
+    "#define STDMETHOD(method) virtual HRESULT STDMETHODCALLTYPE method\n";
+
+#ifndef ICHECK_BUILD
+CppPreprocessor::CppPreprocessor(QPointer<CppModelManager> modelManager)
+    : snapshot(modelManager->snapshot()),
+      m_modelManager(modelManager),
+      preprocess(this, &env),
+      m_revision(0)
+{ }
+
+#else
+
+CppPreprocessor::CppPreprocessor(QPointer<CPlusPlus::ParseManager> modelManager)
+    : preprocess(this, &env),
+      m_revision(0)
+{
+}
+#endif
+
+CppPreprocessor::~CppPreprocessor()
+{ }
+
+void CppPreprocessor::setRevision(unsigned revision)
+{ m_revision = revision; }
+
+void CppPreprocessor::setWorkingCopy(const CppTools::CppModelManagerInterface::WorkingCopy &workingCopy)
+{ m_workingCopy = workingCopy; }
+
+void CppPreprocessor::setIncludePaths(const QStringList &includePaths)
+{ m_includePaths = includePaths; }
+
+void CppPreprocessor::setFrameworkPaths(const QStringList &frameworkPaths)
+{ m_frameworkPaths = frameworkPaths; }
+
+void CppPreprocessor::setProjectFiles(const QStringList &files)
+{ m_projectFiles = files; }
+
+void CppPreprocessor::setTodo(const QStringList &files)
+{ m_todo = QSet<QString>::fromList(files); }
+
+#ifndef ICHECK_BUILD
+namespace {
+class Process: public std::unary_function<Document::Ptr, void>
+{
+    QPointer<CppModelManager> _modelManager;
+    Snapshot _snapshot;
+    CppModelManager::WorkingCopy _workingCopy;
+    Document::Ptr _doc;
+
+public:
+    Process(QPointer<CppModelManager> modelManager,
+            Snapshot snapshot,
+            const CppModelManager::WorkingCopy &workingCopy)
+        : _modelManager(modelManager),
+          _snapshot(snapshot),
+          _workingCopy(workingCopy)
+    { }
+
+    LookupContext lookupContext(unsigned line, unsigned column) const
+    { return lookupContext(_doc->findSymbolAt(line, column)); }
+
+    LookupContext lookupContext(Symbol *symbol) const
+    {
+        LookupContext context(symbol, Document::create(QLatin1String("<none>")), _doc, _snapshot);
+        return context;
+    }
+
+    void operator()(Document::Ptr doc)
+    {
+        _doc = doc;
+
+        Document::CheckMode mode = Document::FastCheck;
+
+        if (_workingCopy.contains(doc->fileName()))
+            mode = Document::FullCheck;
+
+        doc->parse();
+        doc->check(mode);
+
+        if (mode == Document::FullCheck) {
+            // run the binding pass
+            NamespaceBindingPtr ns = bind(doc, _snapshot);
+
+            // check for undefined symbols.
+            CheckUndefinedSymbols checkUndefinedSymbols(doc);
+            checkUndefinedSymbols.setGlobalNamespaceBinding(ns);
+
+            checkUndefinedSymbols(doc->translationUnit()->ast()); // ### FIXME
+        }
+
+        doc->releaseTranslationUnit();
+
+        if (_modelManager)
+            _modelManager->emitDocumentUpdated(doc); // ### TODO: compress
+    }
+};
+} // end of anonymous namespace
+#endif
+
+void CppPreprocessor::run(const QString &fileName)
+{
+    QString absoluteFilePath = fileName;
+    sourceNeeded(absoluteFilePath, IncludeGlobal, /*line = */ 0);
+}
+
+void CppPreprocessor::resetEnvironment()
+{
+    env.reset();
+    m_processed.clear();
+}
+
+bool CppPreprocessor::includeFile(const QString &absoluteFilePath, QString *result, unsigned *revision)
+{
+    if (absoluteFilePath.isEmpty() || m_included.contains(absoluteFilePath))
+        return true;
+
+    if (m_workingCopy.contains(absoluteFilePath)) {
+        m_included.insert(absoluteFilePath);
+        const QPair<QString, unsigned> r = m_workingCopy.get(absoluteFilePath);
+        *result = r.first;
+        *revision = r.second;
+        return true;
+    }
+
+    QFileInfo fileInfo(absoluteFilePath);
+    if (! fileInfo.isFile())
+        return false;
+
+    QFile file(absoluteFilePath);
+    if (file.open(QFile::ReadOnly)) {
+        m_included.insert(absoluteFilePath);
+        QTextStream stream(&file);
+        const QString contents = stream.readAll();
+        *result = contents.toUtf8();
+        file.close();
+        return true;
+    }
+
+    return false;
+}
+
+QString CppPreprocessor::tryIncludeFile(QString &fileName, IncludeType type, unsigned *revision)
+{
+    QFileInfo fileInfo(fileName);
+    if (fileName == QLatin1String(pp_configuration_file) || fileInfo.isAbsolute()) {
+        QString contents;
+        includeFile(fileName, &contents, revision);
+        return contents;
+    }
+
+    if (type == IncludeLocal && m_currentDoc) {
+        QFileInfo currentFileInfo(m_currentDoc->fileName());
+        QString path = currentFileInfo.absolutePath();
+        path += QLatin1Char('/');
+        path += fileName;
+        path = QDir::cleanPath(path);
+        QString contents;
+        if (includeFile(path, &contents, revision)) {
+            fileName = path;
+            return contents;
+        }
+    }
+
+    foreach (const QString &includePath, m_includePaths) {
+        QString path = includePath;
+        path += QLatin1Char('/');
+        path += fileName;
+        path = QDir::cleanPath(path);
+        QString contents;
+        if (includeFile(path, &contents, revision)) {
+            fileName = path;
+            return contents;
+        }
+    }
+
+    // look in the system include paths
+    foreach (const QString &includePath, m_systemIncludePaths) {
+        QString path = includePath;
+        path += QLatin1Char('/');
+        path += fileName;
+        path = QDir::cleanPath(path);
+        QString contents;
+        if (includeFile(path, &contents, revision)) {
+            fileName = path;
+            return contents;
+        }
+    }
+
+    int index = fileName.indexOf(QLatin1Char('/'));
+    if (index != -1) {
+        QString frameworkName = fileName.left(index);
+        QString name = fileName.mid(index + 1);
+
+        foreach (const QString &frameworkPath, m_frameworkPaths) {
+            QString path = frameworkPath;
+            path += QLatin1Char('/');
+            path += frameworkName;
+            path += QLatin1String(".framework/Headers/");
+            path += name;
+            path = QDir::cleanPath(path);
+            QString contents;
+            if (includeFile(path, &contents, revision)) {
+                fileName = path;
+                return contents;
+            }
+        }
+    }
+
+    QString path = fileName;
+    if (path.at(0) != QLatin1Char('/'))
+        path.prepend(QLatin1Char('/'));
+
+    foreach (const QString &projectFile, m_projectFiles) {
+        if (projectFile.endsWith(path)) {
+            fileName = projectFile;
+            QString contents;
+            includeFile(fileName, &contents, revision);
+            return contents;
+        }
+    }
+
+    //qDebug() << "**** file" << fileName << "not found!";
+    return QString();
+}
+
+void CppPreprocessor::macroAdded(const Macro &macro)
+{
+    if (! m_currentDoc)
+        return;
+
+    m_currentDoc->appendMacro(macro);
+}
+
+void CppPreprocessor::passedMacroDefinitionCheck(unsigned offset, const Macro &macro)
+{
+    if (! m_currentDoc)
+        return;
+
+    m_currentDoc->addMacroUse(macro, offset, macro.name().length(), env.currentLine,
+                              QVector<MacroArgumentReference>(), true);
+}
+
+void CppPreprocessor::failedMacroDefinitionCheck(unsigned offset, const QByteArray &name)
+{
+    if (! m_currentDoc)
+        return;
+
+    m_currentDoc->addUndefinedMacroUse(name, offset);
+}
+
+void CppPreprocessor::startExpandingMacro(unsigned offset,
+                                          const Macro &macro,
+                                          const QByteArray &originalText,
+                                          bool inCondition,
+                                          const QVector<MacroArgumentReference> &actuals)
+{
+    if (! m_currentDoc)
+        return;
+
+    //qDebug() << "start expanding:" << macro.name() << "text:" << originalText;
+    m_currentDoc->addMacroUse(macro, offset, originalText.length(), env.currentLine,
+                              actuals, inCondition);
+}
+
+void CppPreprocessor::stopExpandingMacro(unsigned, const Macro &)
+{
+    if (! m_currentDoc)
+        return;
+
+    //qDebug() << "stop expanding:" << macro.name;
+}
+
+void CppPreprocessor::mergeEnvironment(Document::Ptr doc)
+{
+    if (! doc)
+        return;
+
+    const QString fn = doc->fileName();
+
+    if (m_processed.contains(fn))
+        return;
+
+    m_processed.insert(fn);
+
+    foreach (const Document::Include &incl, doc->includes()) {
+        QString includedFile = incl.fileName();
+
+        if (Document::Ptr includedDoc = snapshot.document(includedFile))
+            mergeEnvironment(includedDoc);
+        else
+            run(includedFile);
+    }
+
+    env.addMacros(doc->definedMacros());
+}
+
+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);
+}
+
+void CppPreprocessor::sourceNeeded(QString &fileName, IncludeType type, unsigned line)
+{
+    if (fileName.isEmpty())
+        return;
+
+    unsigned editorRevision = 0;
+    QString contents = tryIncludeFile(fileName, type, &editorRevision);
+    fileName = QDir::cleanPath(fileName);
+    if (m_currentDoc) {
+        m_currentDoc->addIncludeFile(fileName, line);
+
+        if (contents.isEmpty() && ! QFileInfo(fileName).isAbsolute()) {
+            QString msg = QCoreApplication::translate(
+                    "CppPreprocessor", "%1: No such file or directory").arg(fileName);
+
+            Document::DiagnosticMessage d(Document::DiagnosticMessage::Warning,
+                                          m_currentDoc->fileName(),
+                                          env.currentLine, /*column = */ 0,
+                                          msg);
+
+            m_currentDoc->addDiagnosticMessage(d);
+
+            //qWarning() << "file not found:" << fileName << m_currentDoc->fileName() << env.current_line;
+        }
+    }
+
+    //qDebug() << "parse file:" << fileName << "contents:" << contents.size();
+
+    Document::Ptr doc = snapshot.document(fileName);
+    if (doc) {
+        mergeEnvironment(doc);
+        return;
+    }
+
+    doc = Document::create(fileName);
+    doc->setRevision(m_revision);
+    doc->setEditorRevision(editorRevision);
+
+    QFileInfo info(fileName);
+    if (info.exists())
+        doc->setLastModified(info.lastModified());
+
+    Document::Ptr previousDoc = switchDocument(doc);
+
+    const QByteArray preprocessedCode = preprocess(fileName, contents);
+
+    doc->setSource(preprocessedCode);
+    doc->tokenize();
+    doc->releaseSource();
+
+    snapshot.insert(doc);
+    m_todo.remove(fileName);
+
+#ifndef ICHECK_BUILD
+    Process process(m_modelManager, snapshot, m_workingCopy);
+
+    process(doc);
+
+    (void) switchDocument(previousDoc);
+#else
+    (void) switchDocument(previousDoc);
+    Document::CheckMode mode = Document::FastCheck;
+    mode = Document::FullCheck;
+    doc->parse();
+    doc->check(mode);
+#endif
+}
+
+Document::Ptr CppPreprocessor::switchDocument(Document::Ptr doc)
+{
+    Document::Ptr previousDoc = m_currentDoc;
+    m_currentDoc = doc;
+    return previousDoc;
+}
+
+#ifndef ICHECK_BUILD
+void CppTools::CppModelManagerInterface::updateModifiedSourceFiles()
+{
+    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);
+}
+
+CppTools::CppModelManagerInterface *CppTools::CppModelManagerInterface::instance()
+{
+    ExtensionSystem::PluginManager *pluginManager = ExtensionSystem::PluginManager::instance();
+    return pluginManager->getObject<CppTools::CppModelManagerInterface>();
+
+}
+
+
+/*!
+    \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
+    modified within Qt Creator.
+*/
+
+CppModelManager::CppModelManager(QObject *parent)
+    : CppModelManagerInterface(parent)
+{
+    m_findReferences = new CppFindReferences(this);
+    m_indexerEnabled = qgetenv("QTCREATOR_NO_CODE_INDEXER").isNull();
+
+    m_revision = 0;
+    m_synchronizer.setCancelOnWait(true);
+
+    m_core = Core::ICore::instance(); // FIXME
+    m_dirty = true;
+
+    ProjectExplorer::ProjectExplorerPlugin *pe =
+       ProjectExplorer::ProjectExplorerPlugin::instance();
+
+    QTC_ASSERT(pe, return);
+
+    ProjectExplorer::SessionManager *session = pe->session();
+    QTC_ASSERT(session, return);
+
+    m_updateEditorSelectionsTimer = new QTimer(this);
+    m_updateEditorSelectionsTimer->setInterval(500);
+    m_updateEditorSelectionsTimer->setSingleShot(true);
+    connect(m_updateEditorSelectionsTimer, SIGNAL(timeout()),
+            this, SLOT(updateEditorSelections()));
+
+    connect(session, SIGNAL(projectAdded(ProjectExplorer::Project*)),
+            this, SLOT(onProjectAdded(ProjectExplorer::Project*)));
+
+    connect(session, SIGNAL(aboutToRemoveProject(ProjectExplorer::Project *)),
+            this, SLOT(onAboutToRemoveProject(ProjectExplorer::Project *)));
+
+    connect(session, SIGNAL(aboutToUnloadSession()),
+            this, SLOT(onAboutToUnloadSession()));
+
+    qRegisterMetaType<CPlusPlus::Document::Ptr>("CPlusPlus::Document::Ptr");
+
+    // thread connections
+    connect(this, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)),
+            this, SLOT(onDocumentUpdated(CPlusPlus::Document::Ptr)));
+
+    // Listen for editor closed and opened events so that we can keep track of changing files
+    connect(m_core->editorManager(), SIGNAL(editorOpened(Core::IEditor *)),
+        this, SLOT(editorOpened(Core::IEditor *)));
+
+    connect(m_core->editorManager(), SIGNAL(editorAboutToClose(Core::IEditor *)),
+        this, SLOT(editorAboutToClose(Core::IEditor *)));
+}
+
+CppModelManager::~CppModelManager()
+{ }
+
+Snapshot CppModelManager::snapshot() const
+{
+    QMutexLocker locker(&protectSnapshot);
+    return m_snapshot;
+}
+
+void CppModelManager::ensureUpdated()
+{
+    QMutexLocker locker(&mutex);
+    if (! m_dirty)
+        return;
+
+    m_projectFiles = internalProjectFiles();
+    m_includePaths = internalIncludePaths();
+    m_frameworkPaths = internalFrameworkPaths();
+    m_definedMacros = internalDefinedMacros();
+    m_dirty = false;
+}
+
+QStringList CppModelManager::internalProjectFiles() const
+{
+    QStringList files;
+    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects);
+    while (it.hasNext()) {
+        it.next();
+        ProjectInfo pinfo = it.value();
+        files += pinfo.sourceFiles;
+    }
+    files.removeDuplicates();
+    return files;
+}
+
+QStringList CppModelManager::internalIncludePaths() const
+{
+    QStringList includePaths;
+    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects);
+    while (it.hasNext()) {
+        it.next();
+        ProjectInfo pinfo = it.value();
+        includePaths += pinfo.includePaths;
+    }
+    includePaths.removeDuplicates();
+    return includePaths;
+}
+
+QStringList CppModelManager::internalFrameworkPaths() const
+{
+    QStringList frameworkPaths;
+    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects);
+    while (it.hasNext()) {
+        it.next();
+        ProjectInfo pinfo = it.value();
+        frameworkPaths += pinfo.frameworkPaths;
+    }
+    frameworkPaths.removeDuplicates();
+    return frameworkPaths;
+}
+
+QByteArray CppModelManager::internalDefinedMacros() const
+{
+    QByteArray macros;
+    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects);
+    while (it.hasNext()) {
+        it.next();
+        ProjectInfo pinfo = it.value();
+        macros += pinfo.defines;
+    }
+    return macros;
+}
+
+void CppModelManager::setIncludesInPaths(const QMap<QString, QStringList> &includesInPaths)
+{
+    QMutexLocker locker(&mutex);
+    QMapIterator<QString, QStringList> i(includesInPaths);
+    while (i.hasNext()) {
+        i.next();
+        m_includesInPaths.insert(i.key(), i.value());
+    }
+}
+
+void CppModelManager::addEditorSupport(AbstractEditorSupport *editorSupport)
+{
+    m_addtionalEditorSupport.insert(editorSupport);
+}
+
+void CppModelManager::removeEditorSupport(AbstractEditorSupport *editorSupport)
+{
+    m_addtionalEditorSupport.remove(editorSupport);
+}
+
+QList<int> CppModelManager::references(CPlusPlus::Symbol *symbol,
+                                       CPlusPlus::Document::Ptr doc,
+                                       const CPlusPlus::Snapshot &snapshot)
+{
+    NamespaceBindingPtr glo = bind(doc, snapshot);
+    return m_findReferences->references(LookupContext::canonicalSymbol(symbol, glo.data()), doc, snapshot);
+}
+
+void CppModelManager::findUsages(CPlusPlus::Symbol *symbol)
+{
+    if (symbol->identifier())
+        m_findReferences->findUsages(symbol);
+}
+
+void CppModelManager::renameUsages(CPlusPlus::Symbol *symbol)
+{
+    if (symbol->identifier())
+        m_findReferences->renameUsages(symbol);
+}
+
+void CppModelManager::findMacroUsages(const CPlusPlus::Macro &macro)
+{
+    m_findReferences->findMacroUses(macro);
+}
+
+CppModelManager::WorkingCopy CppModelManager::buildWorkingCopyList()
+{
+    WorkingCopy workingCopy;
+    QMapIterator<TextEditor::ITextEditor *, CppEditorSupport *> it(m_editorSupport);
+    while (it.hasNext()) {
+        it.next();
+        TextEditor::ITextEditor *textEditor = it.key();
+        CppEditorSupport *editorSupport = it.value();
+        QString fileName = textEditor->file()->fileName();
+        workingCopy.insert(fileName, editorSupport->contents(), editorSupport->editorRevision());
+    }
+
+    QSetIterator<AbstractEditorSupport *> jt(m_addtionalEditorSupport);
+    while (jt.hasNext()) {
+        AbstractEditorSupport *es =  jt.next();
+        workingCopy.insert(es->fileName(), es->contents());
+    }
+
+    // add the project configuration file
+    QByteArray conf(pp_configuration);
+    conf += definedMacros();
+    workingCopy.insert(pp_configuration_file, conf);
+
+    return workingCopy;
+}
+
+CppModelManager::WorkingCopy CppModelManager::workingCopy() const
+{
+    return const_cast<CppModelManager *>(this)->buildWorkingCopyList();
+}
+
+void CppModelManager::updateSourceFiles(const QStringList &sourceFiles)
+{ (void) refreshSourceFiles(sourceFiles); }
+
+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)
+{
+    QMutexLocker locker(&mutex);
+
+    if (! pinfo.isValid())
+        return;
+
+    m_projects.insert(pinfo.project, pinfo);
+    m_dirty = true;
+
+    if (m_indexerEnabled) {
+        QFuture<void> result = QtConcurrent::run(&CppModelManager::updateIncludesInPaths,
+                                                 this,
+                                                 pinfo.includePaths,
+                                                 pinfo.frameworkPaths,
+                                                 m_headerSuffixes);
+
+        if (pinfo.includePaths.size() > 1) {
+            m_core->progressManager()->addTask(result, tr("Scanning"),
+                                               CppTools::Constants::TASK_INDEX);
+        }
+    }
+}
+
+QStringList CppModelManager::includesInPath(const QString &path) const
+{
+    QMutexLocker locker(&mutex);
+    return m_includesInPaths.value(path);
+}
+
+QFuture<void> CppModelManager::refreshSourceFiles(const QStringList &sourceFiles)
+{
+    if (! sourceFiles.isEmpty() && m_indexerEnabled) {
+        const WorkingCopy workingCopy = buildWorkingCopyList();
+
+        CppPreprocessor *preproc = new CppPreprocessor(this);
+        preproc->setRevision(++m_revision);
+        preproc->setProjectFiles(projectFiles());
+        preproc->setIncludePaths(includePaths());
+        preproc->setFrameworkPaths(frameworkPaths());
+        preproc->setWorkingCopy(workingCopy);
+
+        QFuture<void> result = QtConcurrent::run(&CppModelManager::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) {
+            m_core->progressManager()->addTask(result, tr("Indexing"),
+                            CppTools::Constants::TASK_INDEX);
+        }
+
+        return result;
+    }
+    return QFuture<void>();
+}
+
+/*!
+    \fn    void CppModelManager::editorOpened(Core::IEditor *editor)
+    \brief If a C++ editor is opened, the model manager listens to content changes
+           in order to update the CppCodeModel accordingly. It also updates the
+           CppCodeModel for the first time with this editor.
+
+    \sa    void CppModelManager::editorContentsChanged()
+ */
+void CppModelManager::editorOpened(Core::IEditor *editor)
+{
+    if (isCppEditor(editor)) {
+        TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor *>(editor);
+        QTC_ASSERT(textEditor, return);
+
+        CppEditorSupport *editorSupport = new CppEditorSupport(this);
+        editorSupport->setTextEditor(textEditor);
+        m_editorSupport[textEditor] = editorSupport;
+    }
+}
+
+void CppModelManager::editorAboutToClose(Core::IEditor *editor)
+{
+    if (isCppEditor(editor)) {
+        TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor *>(editor);
+        QTC_ASSERT(textEditor, return);
+
+        CppEditorSupport *editorSupport = m_editorSupport.value(textEditor);
+        m_editorSupport.remove(textEditor);
+        delete editorSupport;
+    }
+}
+
+bool CppModelManager::isCppEditor(Core::IEditor *editor) const
+{
+    Core::UniqueIDManager *uidm = m_core->uniqueIDManager();
+    const int uid = uidm->uniqueIdentifier(ProjectExplorer::Constants::LANG_CXX);
+    return editor->context().contains(uid);
+}
+
+void CppModelManager::emitDocumentUpdated(Document::Ptr doc)
+{ emit documentUpdated(doc); }
+
+void CppModelManager::onDocumentUpdated(Document::Ptr doc)
+{
+    const QString fileName = doc->fileName();
+
+    bool outdated = false;
+
+    protectSnapshot.lock();
+
+    Document::Ptr previous = m_snapshot.document(fileName);
+
+    if (previous && (doc->revision() != 0 && doc->revision() < previous->revision()))
+        outdated = true;
+    else
+        m_snapshot.insert(doc);
+
+    protectSnapshot.unlock();
+
+    if (outdated)
+        return;
+
+    QList<Core::IEditor *> openedEditors = m_core->editorManager()->openedEditors();
+    foreach (Core::IEditor *editor, openedEditors) {
+        if (editor->file()->fileName() == fileName) {
+            TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor *>(editor);
+            if (! textEditor)
+                continue;
+
+            TextEditor::BaseTextEditor *ed = qobject_cast<TextEditor::BaseTextEditor *>(textEditor->widget());
+            if (! ed)
+                continue;
+
+            QList<TextEditor::BaseTextEditor::BlockRange> blockRanges;
+
+            foreach (const Document::Block &block, doc->skippedBlocks()) {
+                blockRanges.append(TextEditor::BaseTextEditor::BlockRange(block.begin(), block.end()));
+            }
+
+            QList<QTextEdit::ExtraSelection> selections;
+
+#ifdef QTCREATOR_WITH_MACRO_HIGHLIGHTING
+            // set up the format for the macros
+            QTextCharFormat macroFormat;
+            macroFormat.setUnderlineStyle(QTextCharFormat::SingleUnderline);
+
+            QTextCursor c = ed->textCursor();
+            foreach (const Document::MacroUse &block, doc->macroUses()) {
+                QTextEdit::ExtraSelection sel;
+                sel.cursor = c;
+                sel.cursor.setPosition(block.begin());
+                sel.cursor.setPosition(block.end(), QTextCursor::KeepAnchor);
+                sel.format = macroFormat;
+                selections.append(sel);
+            }
+#endif // QTCREATOR_WITH_MACRO_HIGHLIGHTING
+
+            // set up the format for the errors
+            QTextCharFormat errorFormat;
+            errorFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline);
+            errorFormat.setUnderlineColor(Qt::red);
+
+            // set up the format for the warnings.
+            QTextCharFormat warningFormat;
+            warningFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline);
+            warningFormat.setUnderlineColor(Qt::darkYellow);
+
+#ifdef QTCREATOR_WITH_ADVANCED_HIGHLIGHTER
+            QSet<QPair<unsigned, unsigned> > lines;
+            foreach (const Document::DiagnosticMessage &m, doc->diagnosticMessages()) {
+                if (m.fileName() != fileName)
+                    continue;
+
+                const QPair<unsigned, unsigned> coordinates = qMakePair(m.line(), m.column());
+
+                if (lines.contains(coordinates))
+                    continue;
+
+                lines.insert(coordinates);
+
+                QTextEdit::ExtraSelection sel;
+                if (m.isWarning())
+                    sel.format = warningFormat;
+                else
+                    sel.format = errorFormat;
+
+                QTextCursor c(ed->document()->findBlockByNumber(m.line() - 1));
+
+                // ### check for generated tokens.
+
+                int column = m.column();
+
+                if (column > c.block().length()) {
+                    column = 0;
+
+                    const QString text = c.block().text();
+                    for (int i = 0; i < text.size(); ++i) {
+                        if (! text.at(i).isSpace()) {
+                            ++column;
+                            break;
+                        }
+                    }
+                }
+
+                if (column != 0)
+                    --column;
+
+                c.setPosition(c.position() + column);
+                c.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
+                sel.cursor = c;
+                selections.append(sel);
+            }
+#else
+            QSet<int> lines;
+            foreach (const Document::DiagnosticMessage &m, doc->diagnosticMessages()) {
+                if (m.fileName() != fileName)
+                    continue;
+                else if (lines.contains(m.line()))
+                    continue;
+
+                lines.insert(m.line());
+
+                QTextEdit::ExtraSelection sel;
+                if (m.isWarning())
+                    sel.format = warningFormat;
+                else
+                    sel.format = errorFormat;
+
+                QTextCursor c(ed->document()->findBlockByNumber(m.line() - 1));
+                const QString text = c.block().text();
+                for (int i = 0; i < text.size(); ++i) {
+                    if (! text.at(i).isSpace()) {
+                        c.setPosition(c.position() + i);
+                        break;
+                    }
+                }
+                c.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
+                sel.cursor = c;
+                selections.append(sel);
+            }
+#endif
+            QList<Editor> todo;
+            foreach (const Editor &e, todo) {
+                if (e.textEditor != textEditor)
+                    todo.append(e);
+            }
+
+            Editor e;
+            e.revision = ed->document()->revision();
+            e.textEditor = textEditor;
+            e.selections = selections;
+            e.ifdefedOutBlocks = blockRanges;
+            todo.append(e);
+            m_todo = todo;
+            postEditorUpdate();
+            break;
+        }
+    }
+}
+
+void CppModelManager::postEditorUpdate()
+{
+    m_updateEditorSelectionsTimer->start(500);
+}
+
+void CppModelManager::updateEditorSelections()
+{
+    foreach (const Editor &ed, m_todo) {
+        if (! ed.textEditor)
+            continue;
+
+        TextEditor::ITextEditor *textEditor = ed.textEditor;
+        TextEditor::BaseTextEditor *editor = qobject_cast<TextEditor::BaseTextEditor *>(textEditor->widget());
+
+        if (! editor)
+            continue;
+        else if (editor->document()->revision() != ed.revision)
+            continue; // outdated
+
+        editor->setExtraSelections(TextEditor::BaseTextEditor::CodeWarningsSelection,
+                                   ed.selections);
+
+        editor->setIfdefedOutBlocks(ed.ifdefedOutBlocks);
+    }
+
+    m_todo.clear();
+
+}
+
+void CppModelManager::onProjectAdded(ProjectExplorer::Project *)
+{
+    QMutexLocker locker(&mutex);
+    m_dirty = true;
+}
+
+void CppModelManager::onAboutToRemoveProject(ProjectExplorer::Project *project)
+{
+    do {
+        QMutexLocker locker(&mutex);
+        m_dirty = true;
+        m_projects.remove(project);
+    } while (0);
+
+    GC();
+}
+
+void CppModelManager::onAboutToUnloadSession()
+{
+    if (m_core->progressManager()) {
+        m_core->progressManager()->cancelTasks(CppTools::Constants::TASK_INDEX);
+    }
+
+    do {
+        QMutexLocker locker(&mutex);
+        m_projects.clear();
+        m_dirty = true;
+    } while (0);
+
+    GC();
+}
+
+void CppModelManager::updateIncludesInPaths(QFutureInterface<void> &future,
+                                            CppModelManager *manager,
+                                            QStringList paths,
+                                            QStringList frameworkPaths,
+                                            QStringList suffixes)
+{
+    QMap<QString, QStringList> entriesInPaths;
+    typedef QPair<QString, QString> SymLink;
+    typedef QList<SymLink> SymLinks;
+    SymLinks symlinks;
+    int processed = 0;
+
+    future.setProgressRange(0, paths.size());
+
+    // Add framework header directories to path list
+    QStringList frameworkFilter;
+    frameworkFilter << QLatin1String("*.framework");
+    QStringListIterator fwPathIt(frameworkPaths);
+    while (fwPathIt.hasNext()) {
+        const QString &fwPath = fwPathIt.next();
+        QStringList entriesInFrameworkPath;
+        const QStringList &frameworks = QDir(fwPath).entryList(frameworkFilter, QDir::Dirs | QDir::NoDotAndDotDot);
+        QStringListIterator fwIt(frameworks);
+        while (fwIt.hasNext()) {
+            QString framework = fwIt.next();
+            paths.append(fwPath + QLatin1Char('/') + framework + QLatin1String("/Headers"));
+            framework.chop(10); // remove the ".framework"
+            entriesInFrameworkPath.append(framework + QLatin1Char('/'));
+        }
+        entriesInPaths.insert(fwPath, entriesInFrameworkPath);
+    }
+
+    while (!paths.isEmpty()) {
+        if (future.isPaused())
+            future.waitForResume();
+
+        if (future.isCanceled())
+            return;
+
+        const QString path = paths.takeFirst();
+
+        // Skip already scanned paths
+        if (entriesInPaths.contains(path))
+            continue;
+
+        QStringList entries;
+
+        QDirIterator i(path, QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
+        while (i.hasNext()) {
+            const QString fileName = i.next();
+            const QFileInfo fileInfo = i.fileInfo();
+            const QString suffix = fileInfo.suffix();
+            if (suffix.isEmpty() || suffixes.contains(suffix)) {
+                QString text = fileInfo.fileName();
+                if (fileInfo.isDir()) {
+                    text += QLatin1Char('/');
+
+                    // Also scan subdirectory, but avoid endless recursion with symbolic links
+                    if (fileInfo.isSymLink()) {
+                        QString target = fileInfo.symLinkTarget();
+
+                        // Don't add broken symlinks
+                        if (!QFileInfo(target).exists())
+                            continue;
+
+                        QMap<QString, QStringList>::const_iterator result = entriesInPaths.find(target);
+                        if (result != entriesInPaths.constEnd()) {
+                            entriesInPaths.insert(fileName, result.value());
+                        } else {
+                            paths.append(target);
+                            symlinks.append(SymLink(fileName, target));
+                        }
+                    } else {
+                        paths.append(fileName);
+                    }
+                }
+                entries.append(text);
+            }
+        }
+
+        entriesInPaths.insert(path, entries);
+
+        ++processed;
+        future.setProgressRange(0, processed + paths.size());
+        future.setProgressValue(processed);
+    }
+    // link symlinks
+    QListIterator<SymLink> it(symlinks);
+    it.toBack();
+    while (it.hasPrevious()) {
+        SymLink v = it.previous();
+        QMap<QString, QStringList>::const_iterator result = entriesInPaths.find(v.second);
+        entriesInPaths.insert(v.first, result.value());
+    }
+
+    manager->setIncludesInPaths(entriesInPaths);
+
+    future.reportFinished();
+}
+
+void CppModelManager::parse(QFutureInterface<void> &future,
+                            CppPreprocessor *preproc,
+                            QStringList files)
+{
+    if (files.isEmpty())
+        return;
+
+    Core::MimeDatabase *db = Core::ICore::instance()->mimeDatabase();
+    QStringList headers, sources;
+    Core::MimeType cSourceTy = db->findByType(QLatin1String("text/x-csrc"));
+    Core::MimeType cppSourceTy = db->findByType(QLatin1String("text/x-c++src"));
+    Core::MimeType mSourceTy = db->findByType(QLatin1String("text/x-objcsrc"));
+
+    Core::MimeType cHeaderTy = db->findByType(QLatin1String("text/x-hdr"));
+    Core::MimeType cppHeaderTy = db->findByType(QLatin1String("text/x-c++hdr"));
+
+    foreach (const QString &file, files) {
+        const QFileInfo fileInfo(file);
+
+        if (cSourceTy.matchesFile(fileInfo) || cppSourceTy.matchesFile(fileInfo) || mSourceTy.matchesFile(fileInfo))
+            sources.append(file);
+
+        else if (cHeaderTy.matchesFile(fileInfo) || cppHeaderTy.matchesFile(fileInfo))
+            headers.append(file);
+    }
+
+    foreach (const QString &file, files) {
+        preproc->snapshot.remove(file);
+    }
+
+    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;
+
+        // Change the priority of the background parser thread to idle.
+        QThread::currentThread()->setPriority(QThread::IdlePriority);
+
+        QString fileName = files.at(i);
+
+        bool isSourceFile = false;
+        if (cppSourceTy.matchesFile(fileName) || cSourceTy.matchesFile(fileName))
+            isSourceFile = true;
+
+        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();
+
+        // Restore the previous thread priority.
+        QThread::currentThread()->setPriority(QThread::NormalPriority);
+    }
+
+    future.setProgressValue(files.size());
+
+    delete preproc;
+}
+
+void CppModelManager::GC()
+{
+    protectSnapshot.lock();
+    Snapshot currentSnapshot = m_snapshot;
+    protectSnapshot.unlock();
+
+    QSet<QString> processed;
+    QStringList todo = projectFiles();
+
+    while (! todo.isEmpty()) {
+        QString fn = todo.last();
+        todo.removeLast();
+
+        if (processed.contains(fn))
+            continue;
+
+        processed.insert(fn);
+
+        if (Document::Ptr doc = currentSnapshot.document(fn)) {
+            todo += doc->includedFiles();
+        }
+    }
+
+    QStringList removedFiles;
+
+    Snapshot newSnapshot;
+    for (Snapshot::const_iterator it = currentSnapshot.begin(); it != currentSnapshot.end(); ++it) {
+        const QString fileName = it.key();
+
+        if (processed.contains(fileName))
+            newSnapshot.insert(it.value());
+        else
+            removedFiles.append(fileName);
+    }
+
+    emit aboutToRemoveFiles(removedFiles);
+
+    protectSnapshot.lock();
+    m_snapshot = newSnapshot;
+    protectSnapshot.unlock();
+}
+#endif
+