Skip to content
Snippets Groups Projects
cppmodelmanager.cpp 42.4 KiB
Newer Older
/**************************************************************************
con's avatar
con committed
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
**
** Contact:  Qt Software Information (qt-info@nokia.com)
**
** Commercial Usage
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
** 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.
** If you are unsure which license is appropriate for your use, please
** contact the sales department at qt-sales@nokia.com.
con's avatar
con committed
**
**************************************************************************/
hjk's avatar
hjk committed

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

#include "cppmodelmanager.h"
#include "cpptoolsconstants.h"
#include "cpptoolseditorsupport.h"

#include <functional>
#include <QtConcurrentRun>
Roberto Raggi's avatar
Roberto Raggi committed
#include <QFutureSynchronizer>
con's avatar
con committed
#include <qtconcurrent/runextensions.h>
con's avatar
con committed
#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>
con's avatar
con committed
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/progressmanager/progressmanager.h>
con's avatar
con committed

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

con's avatar
con committed
#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 <PrettyPrinter.h>
con's avatar
con committed
#include <Lexer.h>
#include <Token.h>

#include <cplusplus/LookupContext.h>

#include <QtCore/QCoreApplication>
hjk's avatar
hjk committed
#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;
con's avatar
con committed
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; }
};

Roberto Raggi's avatar
Roberto Raggi committed
#endif // QTCREATOR_WITH_DUMP_AST
con's avatar
con committed
static const char pp_configuration_file[] = "<configuration>";

static const char pp_configuration[] =
    "# 1 \"<configuration>\"\n"
    "#define __GNUC_MINOR__ 0\n"
    "#define __GNUC__ 4\n"
    "#define __GNUG__ 4\n"
    "#define __STDC_HOSTED__ 1\n"
    "#define __VERSION__ \"4.0.1 (fake)\"\n"
    "#define __cplusplus 1\n"

    "#define __extension__\n"
    "#define __context__\n"
    "#define __range__\n"
    "#define __asm(a...)\n"
    "#define __asm__(a...)\n"
    "#define   restrict\n"
    "#define __restrict\n"

    "#define __complex__\n"
    "#define __imag__\n"
    "#define __real__\n"

con's avatar
con committed
    // ### add macros for win32
    "#define __cdecl\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";

namespace CppTools {
namespace Internal {

class CppPreprocessor: public CPlusPlus::Client
con's avatar
con committed
{
public:
    CppPreprocessor(QPointer<CppModelManager> modelManager);
Roberto Raggi's avatar
Roberto Raggi committed
    virtual ~CppPreprocessor();
con's avatar
con committed

    void setWorkingCopy(const QMap<QString, QByteArray> &workingCopy);
    void setIncludePaths(const QStringList &includePaths);
    void setFrameworkPaths(const QStringList &frameworkPaths);
    void setProjectFiles(const QStringList &files);
    void setTodo(const QStringList &files);

    void run(const QString &fileName);
con's avatar
con committed

    const QSet<QString> &todo() const
    { return m_todo; }

public: // attributes
    Snapshot snapshot;

protected:
    CPlusPlus::Document::Ptr switchDocument(CPlusPlus::Document::Ptr doc);
con's avatar
con committed

    bool includeFile(const QString &absoluteFilePath, QByteArray *result);
    QByteArray tryIncludeFile(QString &fileName, IncludeType type);
con's avatar
con committed

    void mergeEnvironment(CPlusPlus::Document::Ptr doc);
con's avatar
con committed

Roberto Raggi's avatar
Roberto Raggi committed
    virtual void macroAdded(const Macro &macro);
    virtual void startExpandingMacro(unsigned offset,
                                     const Macro &macro,
                                     const QByteArray &originalText,
                                     const QVector<MacroArgumentReference> &actuals);
    virtual void stopExpandingMacro(unsigned offset, const Macro &macro);
    virtual void startSkippingBlocks(unsigned offset);
    virtual void stopSkippingBlocks(unsigned offset);
    virtual void sourceNeeded(QString &fileName, IncludeType type,
                              unsigned line);
con's avatar
con committed

private:
    QPointer<CppModelManager> m_modelManager;
    Environment env;
Roberto Raggi's avatar
Roberto Raggi committed
    Preprocessor preprocess;
    QStringList m_includePaths;
    QStringList m_systemIncludePaths;
    QMap<QString, QByteArray> m_workingCopy;
    QStringList m_projectFiles;
    QStringList m_frameworkPaths;
    QSet<QString> m_included;
    Document::Ptr m_currentDoc;
    QSet<QString> m_todo;
    QSet<QString> m_processed;
con's avatar
con committed

} // namespace Internal
} // namespace CppTools
CppPreprocessor::CppPreprocessor(QPointer<CppModelManager> modelManager)
    : snapshot(modelManager->snapshot()),
      m_modelManager(modelManager),
Roberto Raggi's avatar
Roberto Raggi committed
      preprocess(this, &env)
Roberto Raggi's avatar
Roberto Raggi committed

CppPreprocessor::~CppPreprocessor()
con's avatar
con committed

void CppPreprocessor::setWorkingCopy(const QMap<QString, QByteArray> &workingCopy)
{ m_workingCopy = workingCopy; }
con's avatar
con committed

void CppPreprocessor::setIncludePaths(const QStringList &includePaths)
{ m_includePaths = includePaths; }

void CppPreprocessor::setFrameworkPaths(const QStringList &frameworkPaths)
{ m_frameworkPaths = frameworkPaths; }
con's avatar
con committed

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

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

class Process;

class CheckUndefinedSymbols: protected ASTVisitor
    QSet<QByteArray> _types;
    QSet<QByteArray> _namespaceNames;
    CheckUndefinedSymbols(Document::Ptr doc)
        : ASTVisitor(doc->control()), _process(0), _doc(doc)
    void setGlobalNamespaceBinding(NamespaceBindingPtr globalNamespaceBinding)
    {
        _globalNamespaceBinding = globalNamespaceBinding;
        _types.clear();

        if (_globalNamespaceBinding) {
            QSet<NamespaceBinding *> processed;
            buildTypeMap(_globalNamespaceBinding.data(), &processed);
        }
    }
    void operator()(AST *ast, Process *process)
    { _process = process; accept(ast); }

protected:
    using ASTVisitor::visit;

    void addType(Name *name)
    {
        if (! name)
            return;

        if (Identifier *id = name->identifier())
            _types.insert(QByteArray(id->chars(), id->size()));
    }

    void buildTypeMap(Class *klass)
    {
        addType(klass->name());

        for (unsigned i = 0; i < klass->memberCount(); ++i) {
            Symbol *member = klass->memberAt(i);

            if (Class *klass = member->asClass()) {
                buildTypeMap(klass);
            } else if (Enum *e = member->asEnum()) {
                addType(e->name());
            } else if (ForwardClassDeclaration *fwd = member->asForwardClassDeclaration()) {
                addType(fwd->name());
            } else if (Declaration *decl = member->asDeclaration()) {
                if (decl->isTypedef())
                    addType(decl->name());
            }
        }
    }

    void buildTypeMap(NamespaceBinding *binding, QSet<NamespaceBinding *> *processed)
    {
        if (! processed->contains(binding)) {
            processed->insert(binding);

            if (Identifier *id = binding->identifier()) {
                _namespaceNames.insert(QByteArray(id->chars(), id->size()));
            }

            foreach (Namespace *ns, binding->symbols) {
                for (unsigned i = 0; i < ns->memberCount(); ++i) {
                    Symbol *member = ns->memberAt(i);

                    if (Class *klass = member->asClass()) {
                        buildTypeMap(klass);
                    } else if (Enum *e = member->asEnum()) {
                        addType(e->name());
                    } else if (ForwardClassDeclaration *fwd = member->asForwardClassDeclaration()) {
                        addType(fwd->name());
                    } else if (Declaration *decl = member->asDeclaration()) {
                        if (decl->isTypedef())
                            addType(decl->name());
                    }
                }
            }

            foreach (NamespaceBinding *childBinding, binding->children) {
                buildTypeMap(childBinding, processed);
            }
        }
    }

    QList<FunctionDeclaratorAST *> functionDeclarationStack;

    FunctionDeclaratorAST *currentFunctionDeclarator() const
    {
        if (functionDeclarationStack.isEmpty())
            return 0;

        return functionDeclarationStack.last();
    }

    virtual bool visit(FunctionDeclaratorAST *ast)
    {
        functionDeclarationStack.append(ast);

        return true;
    }

    virtual void endVisit(FunctionDeclaratorAST *)
    {
        functionDeclarationStack.removeLast();
    }

    virtual bool visit(TypeofSpecifierAST *ast)
    {
        accept(ast->next);
        return false;
    }

    virtual bool visit(TypenameTypeParameterAST *ast)
    {
        if (ast->name)
            addType(ast->name->name);

        return true;
    }

    virtual bool visit(NamedTypeSpecifierAST *ast)
    {
        if (ast->name) {
            if (! ast->name->name) {
                unsigned line, col;
                getTokenStartPosition(ast->firstToken(), &line, &col);
                // qWarning() << _doc->fileName() << line << col;
            } else if (Identifier *id = ast->name->name->identifier()) {
                if (! _types.contains(QByteArray::fromRawData(id->chars(), id->size()))) {
                    if (FunctionDeclaratorAST *functionDeclarator = currentFunctionDeclarator()) {
                        if (functionDeclarator->as_cpp_initializer)
                            return true;
                    }

                    Overview oo;
                    translationUnit()->warning(ast->firstToken(), "`%s' is not a type name",
                                               qPrintable(oo(ast->name->name)));
                }
            }
        }
        return true;
    }

    virtual bool visit(ClassSpecifierAST *ast)
    {
        if (ast->base_clause) {
            unsigned line, col;
            getTokenStartPosition(ast->firstToken(), &line, &col);
            _context = lookupContext(line, col);
        }

        bool hasQ_OBJECT_CHECK = false;

        if (ast->symbol) {
            Class *klass = ast->symbol->asClass();

            for (unsigned i = 0; i < klass->memberCount(); ++i) {
                Symbol *symbol = klass->memberAt(i);

                if (symbol->name() && symbol->name()->isNameId()) {
                    NameId *nameId = symbol->name()->asNameId();

                    if (! qstrcmp(nameId->identifier()->chars(), "qt_check_for_QOBJECT_macro")) {
                        hasQ_OBJECT_CHECK = true;
                        break;
                    }
                }
            }
        }

        _qobjectStack.append(hasQ_OBJECT_CHECK);

        return true;
    }

    virtual void endVisit(ClassSpecifierAST *)
    { _qobjectStack.removeLast(); }

    bool qobjectCheck() const
    {
        if (_qobjectStack.isEmpty())
            return false;

        return _qobjectStack.last();
    }

    virtual bool visit(FunctionDefinitionAST *ast)
    {
        if (ast->symbol) {
            Function *fun = ast->symbol->asFunction();
            if ((fun->isSignal() || fun->isSlot()) && ! qobjectCheck()) {
                translationUnit()->warning(ast->firstToken(),
                                           "you forgot the Q_OBJECT macro");
            }
        }
        return true;
    }

    virtual bool visit(SimpleDeclarationAST *ast)
    {
        const bool check = qobjectCheck();
        for (List<Declaration *> *it = ast->symbols; it; it = it->next) {
            Declaration *decl = it->value;

            if (Function *fun = decl->type()->asFunctionType()) {
                if ((fun->isSignal() || fun->isSlot()) && ! check) {
                    translationUnit()->warning(ast->firstToken(),
                                               "you forgot the Q_OBJECT macro");
                }
            }
        }
        return true;
    }

    virtual bool visit(BaseSpecifierAST *base)
    {
        if (base->name) {
            const QList<Symbol *> symbols = _context.resolveClass(base->name->name);

            if (symbols.isEmpty()) {
                const char *token = "after `:'";

                if (base->comma_token)
                    token = "after `,'";

                translationUnit()->warning(base->name->firstToken(),
                                           "expected class-name %s token", token);
            }
        }

        return true;
    }

    virtual bool visit(UsingDirectiveAST *ast)
    {
        if (ast->symbol && ast->symbol->name() && _globalNamespaceBinding) {
            const Location loc = Location(ast->symbol);

            NamespaceBinding *binding = _globalNamespaceBinding.data();

            if (Scope *enclosingNamespaceScope = ast->symbol->enclosingNamespaceScope())
                binding = NamespaceBinding::find(enclosingNamespaceScope->owner()->asNamespace(), binding);

Roberto Raggi's avatar
Roberto Raggi committed
            if (! binding || ! binding->resolveNamespace(loc, ast->symbol->name())) {
                translationUnit()->warning(ast->name->firstToken(),
Roberto Raggi's avatar
Roberto Raggi committed
                                           "expected a namespace");
    virtual bool visit(QualifiedNameAST *ast)
    {
        if (ast->name) {
            QualifiedNameId *q = ast->name->asQualifiedNameId();
            for (unsigned i = 0; i < q->nameCount() - 1; ++i) {
                Name *name = q->nameAt(i);
                if (Identifier *id = name->identifier()) {
                    const QByteArray spell = QByteArray::fromRawData(id->chars(), id->size());
                    if (! (_namespaceNames.contains(spell) || _types.contains(spell))) {
                        translationUnit()->warning(ast->firstToken(),
                                                   "`%s' is not a namespace or class name",
                                                   spell.constData());
                    }
                }
            }
        }

        return true;
    }

    LookupContext lookupContext(unsigned line, unsigned column) const;

private:
    Process *_process;
    LookupContext _context;
    NamespaceBindingPtr _globalNamespaceBinding;
    QList<bool> _qobjectStack;
class Process: public std::unary_function<Document::Ptr, void>
{
    QPointer<CppModelManager> _modelManager;
    Snapshot _snapshot;
    QMap<QString, QByteArray> _workingCopy;
    Document::Ptr _doc;
    Process(QPointer<CppModelManager> modelManager,
            Snapshot snapshot,
            const QMap<QString, QByteArray> &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;
        doc->parse();
        doc->check();

        if (_workingCopy.contains(doc->fileName())) {
Roberto Raggi's avatar
Roberto Raggi committed
            // run the binding pass
            NamespaceBindingPtr ns = bind(doc, _snapshot);

            // check for undefined symbols.
            CheckUndefinedSymbols checkUndefinedSymbols(doc);
            checkUndefinedSymbols.setGlobalNamespaceBinding(ns);
            checkUndefinedSymbols(doc->translationUnit()->ast(), this);
        doc->releaseTranslationUnit();

        if (_modelManager)
            _modelManager->emitDocumentUpdated(doc); // ### TODO: compress
    }
};

LookupContext CheckUndefinedSymbols::lookupContext(unsigned line, unsigned column) const
{ return _process->lookupContext(line, column); }

} // end of anonymous namespace

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, QByteArray *result)
{
    if (absoluteFilePath.isEmpty() || m_included.contains(absoluteFilePath)) {
        return true;
    }

    if (m_workingCopy.contains(absoluteFilePath)) {
        m_included.insert(absoluteFilePath);
        *result = m_workingCopy.value(absoluteFilePath);
        return true;
    }

    QFileInfo fileInfo(absoluteFilePath);
    if (! fileInfo.isFile())
con's avatar
con committed
        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;
con's avatar
con committed
    }

    return false;
}

QByteArray CppPreprocessor::tryIncludeFile(QString &fileName, IncludeType type)
{
    QFileInfo fileInfo(fileName);
    if (fileName == QLatin1String(pp_configuration_file) || fileInfo.isAbsolute()) {
        QByteArray contents;
        includeFile(fileName, &contents);
        return contents;
    }

    if (type == IncludeLocal && m_currentDoc) {
        QFileInfo currentFileInfo(m_currentDoc->fileName());
        QString path = currentFileInfo.absolutePath();
        path += QLatin1Char('/');
        path += fileName;
        path = QDir::cleanPath(path);
        QByteArray contents;
        if (includeFile(path, &contents)) {
            fileName = path;
con's avatar
con committed
            return contents;
        }
con's avatar
con committed

    foreach (const QString &includePath, m_includePaths) {
        QString path = includePath;
        path += QLatin1Char('/');
        path += fileName;
        path = QDir::cleanPath(path);
        QByteArray contents;
        if (includeFile(path, &contents)) {
            fileName = path;
            return contents;
con's avatar
con committed
        }
con's avatar
con committed

    // look in the system include paths
    foreach (const QString &includePath, m_systemIncludePaths) {
        QString path = includePath;
        path += QLatin1Char('/');
        path += fileName;
        path = QDir::cleanPath(path);
        QByteArray contents;
        if (includeFile(path, &contents)) {
            fileName = path;
            return contents;
con's avatar
con committed
        }
    }

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

        foreach (const QString &frameworkPath, m_frameworkPaths) {
            QString path = frameworkPath;
con's avatar
con committed
            path += QLatin1Char('/');
            path += frameworkName;
            path += QLatin1String(".framework/Headers/");
            path += name;
            path = QDir::cleanPath(path);
con's avatar
con committed
            QByteArray contents;
            if (includeFile(path, &contents)) {
                fileName = path;
                return contents;
            }
        }
con's avatar
con committed

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

    foreach (const QString &projectFile, m_projectFiles) {
        if (projectFile.endsWith(path)) {
            fileName = projectFile;
            QByteArray contents;
            includeFile(fileName, &contents);
            return contents;
con's avatar
con committed
        }
    }

    //qDebug() << "**** file" << fileName << "not found!";
    return QByteArray();
}
con's avatar
con committed

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

Roberto Raggi's avatar
Roberto Raggi committed
    m_currentDoc->appendMacro(macro);
void CppPreprocessor::startExpandingMacro(unsigned offset,
                                          const Macro &macro,
                                          const QByteArray &originalText,
                                          const QVector<MacroArgumentReference> &actuals)
{
    if (! m_currentDoc)
        return;
    //qDebug() << "start expanding:" << macro.name << "text:" << originalText;
    m_currentDoc->addMacroUse(macro, offset, originalText.length(), actuals);
void CppPreprocessor::stopExpandingMacro(unsigned, const Macro &)
{
    if (! m_currentDoc)
        return;
con's avatar
con committed

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

void CppPreprocessor::mergeEnvironment(Document::Ptr doc)
{
    if (! doc)
        return;
con's avatar
con committed

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

    if (m_processed.contains(fn))
con's avatar
con committed

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

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

        if (Document::Ptr includedDoc = snapshot.value(includedFile))
            mergeEnvironment(includedDoc);
Roberto Raggi's avatar
Roberto Raggi committed
    }
con's avatar
con committed

con's avatar
con committed

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;

    QByteArray contents = tryIncludeFile(fileName, type);

    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;
con's avatar
con committed
        }
con's avatar
con committed

    //qDebug() << "parse file:" << fileName << "contents:" << contents.size();
con's avatar
con committed

    Document::Ptr doc = snapshot.value(fileName);
    if (doc) {
        mergeEnvironment(doc);
        return;
    }
con's avatar
con committed

con's avatar
con committed

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

Roberto Raggi's avatar
Roberto Raggi committed
    const QByteArray preprocessedCode = preprocess(fileName.toUtf8(), contents);
con's avatar
con committed

    doc->tokenize();
    doc->releaseSource();
    snapshot.insert(doc->fileName(), doc);
con's avatar
con committed

    Process process(m_modelManager, snapshot, m_workingCopy);
con's avatar
con committed

Document::Ptr CppPreprocessor::switchDocument(Document::Ptr doc)
{
    Document::Ptr previousDoc = m_currentDoc;
    m_currentDoc = doc;
    return previousDoc;
}
con's avatar
con committed



/*!
    \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.
con's avatar
con committed
*/

CppModelManager::CppModelManager(QObject *parent)
    : CppModelManagerInterface(parent)
con's avatar
con committed
{
Roberto Raggi's avatar
Roberto Raggi committed
    m_synchronizer.setCancelOnWait(true);

    m_core = Core::ICore::instance(); // FIXME
    m_dirty = true;

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

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

    ProjectExplorer::SessionManager *session = pe->session();
hjk's avatar
hjk committed
    QTC_ASSERT(session, return);
con's avatar
con committed

    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*)));

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

    connect(session, SIGNAL(sessionUnloaded()),
            this, SLOT(onSessionUnloaded()));

    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()
{ }

    QMutexLocker locker(&protectSnapshot);
con's avatar
con committed

void CppModelManager::ensureUpdated()
{
    QMutexLocker locker(&mutex);
    if (! m_dirty)
        return;

    m_projectFiles = internalProjectFiles();
    m_includePaths = internalIncludePaths();
    m_frameworkPaths = internalFrameworkPaths();
    m_definedMacros = internalDefinedMacros();
QStringList CppModelManager::internalProjectFiles() const
con's avatar
con committed
{
    QStringList files;
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects);
    while (it.hasNext()) {
        it.next();
        ProjectInfo pinfo = it.value();
        files += pinfo.sourceFiles;
    }
    files.removeDuplicates();
con's avatar
con committed
    return files;
}

QStringList CppModelManager::internalIncludePaths() const
con's avatar
con committed
{
    QStringList includePaths;
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects);
    while (it.hasNext()) {
        it.next();
        ProjectInfo pinfo = it.value();
        includePaths += pinfo.includePaths;
    }
    includePaths.removeDuplicates();
con's avatar
con committed
    return includePaths;
}

QStringList CppModelManager::internalFrameworkPaths() const
con's avatar
con committed
{
    QStringList frameworkPaths;
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects);
    while (it.hasNext()) {
        it.next();
        ProjectInfo pinfo = it.value();
        frameworkPaths += pinfo.frameworkPaths;
    }
    frameworkPaths.removeDuplicates();
con's avatar
con committed
    return frameworkPaths;
}

QByteArray CppModelManager::internalDefinedMacros() const
con's avatar
con committed
{
    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::addEditorSupport(AbstractEditorSupport *editorSupport)
{
    m_addtionalEditorSupport.insert(editorSupport);
}

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

QMap<QString, QByteArray> CppModelManager::buildWorkingCopyList()
con's avatar
con committed
{
    QMap<QString, QByteArray> 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();
con's avatar
con committed
    }

    QSetIterator<AbstractEditorSupport *> jt(m_addtionalEditorSupport);
    while (jt.hasNext()) {