forked from qt-creator/qt-creator
		
	The m_parserFuture.cancel() in ~BuiltinEditorDocumentProcessor() did not cancel anything. Thus, closing a document while the parser was running led to a blocking UI thread. Now it cancels at the next include directive it encounters. Change-Id: I092fddbbd747e0bc95265b6e9b4fcc26b3f76cb3 Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io> Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
		
			
				
	
	
		
			514 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			514 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/****************************************************************************
 | 
						|
**
 | 
						|
** Copyright (C) 2016 The Qt Company Ltd.
 | 
						|
** Contact: https://www.qt.io/licensing/
 | 
						|
**
 | 
						|
** This file is part of Qt Creator.
 | 
						|
**
 | 
						|
** 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 The Qt Company. For licensing terms
 | 
						|
** and conditions see https://www.qt.io/terms-conditions. For further
 | 
						|
** information use the contact form at https://www.qt.io/contact-us.
 | 
						|
**
 | 
						|
** GNU General Public License Usage
 | 
						|
** Alternatively, this file may be used under the terms of the GNU
 | 
						|
** General Public License version 3 as published by the Free Software
 | 
						|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
 | 
						|
** included in the packaging of this file. Please review the following
 | 
						|
** information to ensure the GNU General Public License requirements will
 | 
						|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
 | 
						|
**
 | 
						|
****************************************************************************/
 | 
						|
 | 
						|
#include "cppsourceprocessor.h"
 | 
						|
 | 
						|
#include "cppmodelmanager.h"
 | 
						|
#include "cpptoolsreuse.h"
 | 
						|
 | 
						|
#include <coreplugin/editormanager/editormanager.h>
 | 
						|
 | 
						|
#include <utils/fileutils.h>
 | 
						|
#include <utils/hostosinfo.h>
 | 
						|
#include <utils/qtcassert.h>
 | 
						|
#include <utils/textfileformat.h>
 | 
						|
 | 
						|
#include <QCoreApplication>
 | 
						|
#include <QCryptographicHash>
 | 
						|
#include <QDir>
 | 
						|
#include <QLoggingCategory>
 | 
						|
#include <QTextCodec>
 | 
						|
 | 
						|
/*!
 | 
						|
 * \class CppTools::Internal::CppSourceProcessor
 | 
						|
 * \brief The CppSourceProcessor class updates set of indexed C++ files.
 | 
						|
 *
 | 
						|
 * Working copy ensures that documents with most recent copy placed in memory will be parsed
 | 
						|
 * correctly.
 | 
						|
 *
 | 
						|
 * \sa CPlusPlus::Document
 | 
						|
 * \sa CppTools::WorkingCopy
 | 
						|
 */
 | 
						|
 | 
						|
using namespace CPlusPlus;
 | 
						|
using namespace CppTools;
 | 
						|
using namespace CppTools::Internal;
 | 
						|
 | 
						|
typedef Document::DiagnosticMessage Message;
 | 
						|
 | 
						|
static Q_LOGGING_CATEGORY(log, "qtc.cpptools.sourceprocessor")
 | 
						|
 | 
						|
namespace {
 | 
						|
 | 
						|
inline QByteArray generateFingerPrint(const QList<Macro> &definedMacros, const QByteArray &code)
 | 
						|
{
 | 
						|
    QCryptographicHash hash(QCryptographicHash::Sha1);
 | 
						|
    hash.addData(code);
 | 
						|
    foreach (const Macro ¯o, definedMacros) {
 | 
						|
        if (macro.isHidden()) {
 | 
						|
            static const QByteArray undef("#undef ");
 | 
						|
            hash.addData(undef);
 | 
						|
            hash.addData(macro.name());
 | 
						|
        } else {
 | 
						|
            static const QByteArray def("#define ");
 | 
						|
            hash.addData(macro.name());
 | 
						|
            hash.addData(" ", 1);
 | 
						|
            hash.addData(def);
 | 
						|
            hash.addData(macro.definitionText());
 | 
						|
        }
 | 
						|
        hash.addData("\n", 1);
 | 
						|
    }
 | 
						|
    return hash.result();
 | 
						|
}
 | 
						|
 | 
						|
inline Message messageNoSuchFile(Document::Ptr &document, const QString &fileName, unsigned line)
 | 
						|
{
 | 
						|
    const QString text = QCoreApplication::translate(
 | 
						|
        "CppSourceProcessor", "%1: No such file or directory").arg(fileName);
 | 
						|
    return Message(Message::Warning, document->fileName(), line, /*column =*/ 0, text);
 | 
						|
}
 | 
						|
 | 
						|
inline Message messageNoFileContents(Document::Ptr &document, const QString &fileName,
 | 
						|
                                     unsigned line)
 | 
						|
{
 | 
						|
    const QString text = QCoreApplication::translate(
 | 
						|
        "CppSourceProcessor", "%1: Could not get file contents").arg(fileName);
 | 
						|
    return Message(Message::Warning, document->fileName(), line, /*column =*/ 0, text);
 | 
						|
}
 | 
						|
 | 
						|
inline const Macro revision(const WorkingCopy &workingCopy,
 | 
						|
                            const Macro ¯o)
 | 
						|
{
 | 
						|
    Macro newMacro(macro);
 | 
						|
    newMacro.setFileRevision(workingCopy.get(macro.fileName()).second);
 | 
						|
    return newMacro;
 | 
						|
}
 | 
						|
 | 
						|
} // anonymous namespace
 | 
						|
 | 
						|
CppSourceProcessor::CppSourceProcessor(const Snapshot &snapshot, DocumentCallback documentFinished)
 | 
						|
    : m_snapshot(snapshot),
 | 
						|
      m_documentFinished(documentFinished),
 | 
						|
      m_preprocess(this, &m_env),
 | 
						|
      m_languageFeatures(LanguageFeatures::defaultFeatures()),
 | 
						|
      m_defaultCodec(Core::EditorManager::defaultTextCodec())
 | 
						|
{
 | 
						|
    m_preprocess.setKeepComments(true);
 | 
						|
}
 | 
						|
 | 
						|
CppSourceProcessor::~CppSourceProcessor()
 | 
						|
{ }
 | 
						|
 | 
						|
void CppSourceProcessor::setCancelChecker(const CppSourceProcessor::CancelChecker &cancelChecker)
 | 
						|
{
 | 
						|
    m_preprocess.setCancelChecker(cancelChecker);
 | 
						|
}
 | 
						|
 | 
						|
void CppSourceProcessor::setWorkingCopy(const WorkingCopy &workingCopy)
 | 
						|
{ m_workingCopy = workingCopy; }
 | 
						|
 | 
						|
void CppSourceProcessor::setHeaderPaths(const ProjectPartHeaderPaths &headerPaths)
 | 
						|
{
 | 
						|
    m_headerPaths.clear();
 | 
						|
 | 
						|
    for (int i = 0, ei = headerPaths.size(); i < ei; ++i) {
 | 
						|
        const ProjectPartHeaderPath &path = headerPaths.at(i);
 | 
						|
 | 
						|
        if (path.type == ProjectPartHeaderPath::IncludePath)
 | 
						|
            m_headerPaths.append(ProjectPartHeaderPath(cleanPath(path.path), path.type));
 | 
						|
        else
 | 
						|
            addFrameworkPath(path);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void CppSourceProcessor::setLanguageFeatures(const LanguageFeatures languageFeatures)
 | 
						|
{
 | 
						|
    m_languageFeatures = languageFeatures;
 | 
						|
}
 | 
						|
 | 
						|
// 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 CppSourceProcessor::addFrameworkPath(const ProjectPartHeaderPath &frameworkPath)
 | 
						|
{
 | 
						|
    QTC_ASSERT(frameworkPath.isFrameworkPath(), return);
 | 
						|
 | 
						|
    // 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.
 | 
						|
    const ProjectPartHeaderPath cleanFrameworkPath(cleanPath(frameworkPath.path),
 | 
						|
                                                   frameworkPath.type);
 | 
						|
    if (!m_headerPaths.contains(cleanFrameworkPath))
 | 
						|
        m_headerPaths.append(cleanFrameworkPath);
 | 
						|
 | 
						|
    const QDir frameworkDir(cleanFrameworkPath.path);
 | 
						|
    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(ProjectPartHeaderPath(privateFrameworks.absoluteFilePath(),
 | 
						|
                                                   frameworkPath.type));
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void CppSourceProcessor::setTodo(const QSet<QString> &files)
 | 
						|
{
 | 
						|
    m_todo = files;
 | 
						|
}
 | 
						|
 | 
						|
void CppSourceProcessor::run(const QString &fileName,
 | 
						|
                             const QStringList &initialIncludes)
 | 
						|
{
 | 
						|
    sourceNeeded(0, fileName, IncludeGlobal, initialIncludes);
 | 
						|
}
 | 
						|
 | 
						|
void CppSourceProcessor::removeFromCache(const QString &fileName)
 | 
						|
{
 | 
						|
    m_snapshot.remove(fileName);
 | 
						|
}
 | 
						|
 | 
						|
void CppSourceProcessor::resetEnvironment()
 | 
						|
{
 | 
						|
    m_env.reset();
 | 
						|
    m_processed.clear();
 | 
						|
    m_included.clear();
 | 
						|
}
 | 
						|
 | 
						|
bool CppSourceProcessor::getFileContents(const QString &absoluteFilePath,
 | 
						|
                                         QByteArray *contents,
 | 
						|
                                         unsigned *revision) const
 | 
						|
{
 | 
						|
    if (absoluteFilePath.isEmpty() || !contents || !revision)
 | 
						|
        return false;
 | 
						|
 | 
						|
    // Get from working copy
 | 
						|
    if (m_workingCopy.contains(absoluteFilePath)) {
 | 
						|
        const QPair<QByteArray, unsigned> entry = m_workingCopy.get(absoluteFilePath);
 | 
						|
        *contents = entry.first;
 | 
						|
        *revision = entry.second;
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    // Get from file
 | 
						|
    *revision = 0;
 | 
						|
    QString error;
 | 
						|
    if (Utils::TextFileFormat::readFileUTF8(absoluteFilePath, m_defaultCodec, contents, &error)
 | 
						|
            != Utils::TextFileFormat::ReadSuccess) {
 | 
						|
        qWarning("Error reading file \"%s\": \"%s\".", qPrintable(absoluteFilePath),
 | 
						|
                 qPrintable(error));
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
bool CppSourceProcessor::checkFile(const QString &absoluteFilePath) const
 | 
						|
{
 | 
						|
    if (absoluteFilePath.isEmpty()
 | 
						|
            || m_included.contains(absoluteFilePath)
 | 
						|
            || m_workingCopy.contains(absoluteFilePath)) {
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    const QFileInfo fileInfo(absoluteFilePath);
 | 
						|
    return fileInfo.isFile() && fileInfo.isReadable();
 | 
						|
}
 | 
						|
 | 
						|
QString CppSourceProcessor::cleanPath(const QString &path)
 | 
						|
{
 | 
						|
    QString result = QDir::cleanPath(path);
 | 
						|
    const QChar slash(QLatin1Char('/'));
 | 
						|
    if (!result.endsWith(slash))
 | 
						|
        result.append(slash);
 | 
						|
    return result;
 | 
						|
}
 | 
						|
 | 
						|
/// Resolve the given file name to its absolute path w.r.t. the include type.
 | 
						|
QString CppSourceProcessor::resolveFile(const QString &fileName, IncludeType type)
 | 
						|
{
 | 
						|
    if (isInjectedFile(fileName))
 | 
						|
        return fileName;
 | 
						|
 | 
						|
    if (QFileInfo(fileName).isAbsolute())
 | 
						|
        return checkFile(fileName) ? fileName : QString();
 | 
						|
 | 
						|
    if (m_currentDoc) {
 | 
						|
        if (type == IncludeLocal) {
 | 
						|
            const QFileInfo currentFileInfo(m_currentDoc->fileName());
 | 
						|
            const QString path = cleanPath(currentFileInfo.absolutePath()) + fileName;
 | 
						|
            if (checkFile(path))
 | 
						|
                return path;
 | 
						|
            // Fall through! "16.2 Source file inclusion" from the standard states to continue
 | 
						|
            // searching as if this would be a global include.
 | 
						|
 | 
						|
        } else if (type == IncludeNext) {
 | 
						|
            const QFileInfo currentFileInfo(m_currentDoc->fileName());
 | 
						|
            const QString currentDirPath = cleanPath(currentFileInfo.dir().path());
 | 
						|
            auto headerPathsEnd = m_headerPaths.end();
 | 
						|
            auto headerPathsIt = m_headerPaths.begin();
 | 
						|
            for (; headerPathsIt != headerPathsEnd; ++headerPathsIt) {
 | 
						|
                if (headerPathsIt->path == currentDirPath) {
 | 
						|
                    ++headerPathsIt;
 | 
						|
                    return resolveFile_helper(fileName, headerPathsIt);
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    QHash<QString, QString>::ConstIterator it = m_fileNameCache.constFind(fileName);
 | 
						|
    if (it != m_fileNameCache.constEnd())
 | 
						|
        return it.value();
 | 
						|
    const QString fn = resolveFile_helper(fileName, m_headerPaths.begin());
 | 
						|
    if (!fn.isEmpty())
 | 
						|
        m_fileNameCache.insert(fileName, fn);
 | 
						|
    return fn;
 | 
						|
}
 | 
						|
 | 
						|
QString CppSourceProcessor::resolveFile_helper(const QString &fileName,
 | 
						|
                                               ProjectPartHeaderPaths::Iterator headerPathsIt)
 | 
						|
{
 | 
						|
    auto headerPathsEnd = m_headerPaths.end();
 | 
						|
    const int index = fileName.indexOf(QLatin1Char('/'));
 | 
						|
    for (; headerPathsIt != headerPathsEnd; ++headerPathsIt) {
 | 
						|
        if (headerPathsIt->isValid()) {
 | 
						|
            QString path;
 | 
						|
            if (headerPathsIt->isFrameworkPath()) {
 | 
						|
                if (index == -1)
 | 
						|
                    continue;
 | 
						|
                path = headerPathsIt->path + fileName.left(index)
 | 
						|
                       + QLatin1String(".framework/Headers/") + fileName.mid(index + 1);
 | 
						|
            } else {
 | 
						|
                path = headerPathsIt->path + fileName;
 | 
						|
            }
 | 
						|
            if (m_workingCopy.contains(path) || checkFile(path))
 | 
						|
                return path;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return QString();
 | 
						|
}
 | 
						|
 | 
						|
void CppSourceProcessor::macroAdded(const Macro ¯o)
 | 
						|
{
 | 
						|
    if (!m_currentDoc)
 | 
						|
        return;
 | 
						|
 | 
						|
    m_currentDoc->appendMacro(macro);
 | 
						|
}
 | 
						|
 | 
						|
void CppSourceProcessor::passedMacroDefinitionCheck(unsigned bytesOffset, unsigned utf16charsOffset,
 | 
						|
                                                    unsigned line, const Macro ¯o)
 | 
						|
{
 | 
						|
    if (!m_currentDoc)
 | 
						|
        return;
 | 
						|
 | 
						|
    m_currentDoc->addMacroUse(revision(m_workingCopy, macro),
 | 
						|
                              bytesOffset, macro.name().length(),
 | 
						|
                              utf16charsOffset, macro.nameToQString().size(),
 | 
						|
                              line, QVector<MacroArgumentReference>());
 | 
						|
}
 | 
						|
 | 
						|
void CppSourceProcessor::failedMacroDefinitionCheck(unsigned bytesOffset, unsigned utf16charOffset,
 | 
						|
                                                    const ByteArrayRef &name)
 | 
						|
{
 | 
						|
    if (!m_currentDoc)
 | 
						|
        return;
 | 
						|
 | 
						|
    m_currentDoc->addUndefinedMacroUse(QByteArray(name.start(), name.size()),
 | 
						|
                                       bytesOffset, utf16charOffset);
 | 
						|
}
 | 
						|
 | 
						|
void CppSourceProcessor::notifyMacroReference(unsigned bytesOffset, unsigned utf16charOffset,
 | 
						|
                                              unsigned line, const Macro ¯o)
 | 
						|
{
 | 
						|
    if (!m_currentDoc)
 | 
						|
        return;
 | 
						|
 | 
						|
    m_currentDoc->addMacroUse(revision(m_workingCopy, macro),
 | 
						|
                              bytesOffset, macro.name().length(),
 | 
						|
                              utf16charOffset, macro.nameToQString().size(),
 | 
						|
                              line, QVector<MacroArgumentReference>());
 | 
						|
}
 | 
						|
 | 
						|
void CppSourceProcessor::startExpandingMacro(unsigned bytesOffset, unsigned utf16charOffset,
 | 
						|
                                             unsigned line, const Macro ¯o,
 | 
						|
                                             const QVector<MacroArgumentReference> &actuals)
 | 
						|
{
 | 
						|
    if (!m_currentDoc)
 | 
						|
        return;
 | 
						|
 | 
						|
    m_currentDoc->addMacroUse(revision(m_workingCopy, macro),
 | 
						|
                              bytesOffset, macro.name().length(),
 | 
						|
                              utf16charOffset, macro.nameToQString().size(),
 | 
						|
                              line, actuals);
 | 
						|
}
 | 
						|
 | 
						|
void CppSourceProcessor::stopExpandingMacro(unsigned, const Macro &)
 | 
						|
{
 | 
						|
    if (!m_currentDoc)
 | 
						|
        return;
 | 
						|
}
 | 
						|
 | 
						|
void CppSourceProcessor::markAsIncludeGuard(const QByteArray ¯oName)
 | 
						|
{
 | 
						|
    if (!m_currentDoc)
 | 
						|
        return;
 | 
						|
 | 
						|
    m_currentDoc->setIncludeGuardMacroName(macroName);
 | 
						|
}
 | 
						|
 | 
						|
void CppSourceProcessor::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->resolvedIncludes()) {
 | 
						|
        const QString includedFile = incl.resolvedFileName();
 | 
						|
 | 
						|
        if (Document::Ptr includedDoc = m_snapshot.document(includedFile))
 | 
						|
            mergeEnvironment(includedDoc);
 | 
						|
        else if (!m_included.contains(includedFile))
 | 
						|
            run(includedFile);
 | 
						|
    }
 | 
						|
 | 
						|
    m_env.addMacros(doc->definedMacros());
 | 
						|
}
 | 
						|
 | 
						|
void CppSourceProcessor::startSkippingBlocks(unsigned utf16charsOffset)
 | 
						|
{
 | 
						|
    if (m_currentDoc)
 | 
						|
        m_currentDoc->startSkippingBlocks(utf16charsOffset);
 | 
						|
}
 | 
						|
 | 
						|
void CppSourceProcessor::stopSkippingBlocks(unsigned utf16charsOffset)
 | 
						|
{
 | 
						|
    if (m_currentDoc)
 | 
						|
        m_currentDoc->stopSkippingBlocks(utf16charsOffset);
 | 
						|
}
 | 
						|
 | 
						|
void CppSourceProcessor::sourceNeeded(unsigned line, const QString &fileName, IncludeType type,
 | 
						|
                                      const QStringList &initialIncludes)
 | 
						|
{
 | 
						|
    if (fileName.isEmpty())
 | 
						|
        return;
 | 
						|
 | 
						|
    QString absoluteFileName = resolveFile(fileName, type);
 | 
						|
    absoluteFileName = QDir::cleanPath(absoluteFileName);
 | 
						|
    if (m_currentDoc) {
 | 
						|
        m_currentDoc->addIncludeFile(Document::Include(fileName, absoluteFileName, line, type));
 | 
						|
        if (absoluteFileName.isEmpty()) {
 | 
						|
            m_currentDoc->addDiagnosticMessage(messageNoSuchFile(m_currentDoc, fileName, line));
 | 
						|
            return;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    if (m_included.contains(absoluteFileName))
 | 
						|
        return; // We've already seen this file.
 | 
						|
    if (!isInjectedFile(absoluteFileName))
 | 
						|
        m_included.insert(absoluteFileName);
 | 
						|
 | 
						|
    // Already in snapshot? Use it!
 | 
						|
    if (Document::Ptr document = m_snapshot.document(absoluteFileName)) {
 | 
						|
        mergeEnvironment(document);
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    const QFileInfo info(absoluteFileName);
 | 
						|
    if (skipFileDueToSizeLimit(info))
 | 
						|
        return; // TODO: Add diagnostic message
 | 
						|
 | 
						|
    // Otherwise get file contents
 | 
						|
    unsigned editorRevision = 0;
 | 
						|
    QByteArray contents;
 | 
						|
    const bool gotFileContents = getFileContents(absoluteFileName, &contents, &editorRevision);
 | 
						|
    if (m_currentDoc && !gotFileContents) {
 | 
						|
        m_currentDoc->addDiagnosticMessage(messageNoFileContents(m_currentDoc, fileName, line));
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    qCDebug(log) << "Parsing:" << absoluteFileName << "contents:" << contents.size() << "bytes";
 | 
						|
 | 
						|
    Document::Ptr document = Document::create(absoluteFileName);
 | 
						|
    document->setEditorRevision(editorRevision);
 | 
						|
    document->setLanguageFeatures(m_languageFeatures);
 | 
						|
    foreach (const QString &include, initialIncludes) {
 | 
						|
        m_included.insert(include);
 | 
						|
        Document::Include inc(include, include, 0, IncludeLocal);
 | 
						|
        document->addIncludeFile(inc);
 | 
						|
    }
 | 
						|
    if (info.exists())
 | 
						|
        document->setLastModified(info.lastModified());
 | 
						|
 | 
						|
    const Document::Ptr previousDocument = switchCurrentDocument(document);
 | 
						|
    const QByteArray preprocessedCode = m_preprocess.run(absoluteFileName, contents);
 | 
						|
//    {
 | 
						|
//        QByteArray b(preprocessedCode); b.replace("\n", "<<<\n");
 | 
						|
//        qDebug("Preprocessed code for \"%s\": [[%s]]", fileName.toUtf8().constData(), b.constData());
 | 
						|
//    }
 | 
						|
    document->setFingerprint(generateFingerPrint(document->definedMacros(), preprocessedCode));
 | 
						|
 | 
						|
    // Re-use document from global snapshot if possible
 | 
						|
    Document::Ptr globalDocument = m_globalSnapshot.document(absoluteFileName);
 | 
						|
    if (globalDocument && globalDocument->fingerprint() == document->fingerprint()) {
 | 
						|
        switchCurrentDocument(previousDocument);
 | 
						|
        mergeEnvironment(globalDocument);
 | 
						|
        m_snapshot.insert(globalDocument);
 | 
						|
        m_todo.remove(absoluteFileName);
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    // Otherwise process the document
 | 
						|
    document->setUtf8Source(preprocessedCode);
 | 
						|
    document->keepSourceAndAST();
 | 
						|
    document->tokenize();
 | 
						|
    document->check(m_workingCopy.contains(document->fileName()) ? Document::FullCheck
 | 
						|
                                                                 : Document::FastCheck);
 | 
						|
 | 
						|
    m_documentFinished(document);
 | 
						|
 | 
						|
    m_snapshot.insert(document);
 | 
						|
    m_todo.remove(absoluteFileName);
 | 
						|
    switchCurrentDocument(previousDocument);
 | 
						|
}
 | 
						|
 | 
						|
Document::Ptr CppSourceProcessor::switchCurrentDocument(Document::Ptr doc)
 | 
						|
{
 | 
						|
    const Document::Ptr previousDoc = m_currentDoc;
 | 
						|
    m_currentDoc = doc;
 | 
						|
    return previousDoc;
 | 
						|
}
 |