diff --git a/src/libs/cplusplus/ASTPath.cpp b/src/libs/cplusplus/ASTPath.cpp index 0d3fcf726d7ecfa6b4c6e28a44cafb86ffdef661..e4dc4a1bbcc3db139d4d2149f4632858028eb75a 100644 --- a/src/libs/cplusplus/ASTPath.cpp +++ b/src/libs/cplusplus/ASTPath.cpp @@ -32,10 +32,10 @@ #include <cplusplus/AST.h> #include <cplusplus/TranslationUnit.h> -#ifdef DEBUG_AST_PATH +#ifdef WITH_AST_PATH_DUMP # include <QDebug> # include <typeinfo> -#endif // DEBUG_AST_PATH +#endif // WITH_AST_PATH_DUMP using namespace CPlusPlus; @@ -53,14 +53,14 @@ QList<AST *> ASTPath::operator()(int line, int column) return _nodes; } -#ifdef DEBUG_AST_PATH +#ifdef WITH_AST_PATH_DUMP void ASTPath::dump(const QList<AST *> nodes) { qDebug() << "ASTPath dump," << nodes.size() << "nodes:"; for (int i = 0; i < nodes.size(); ++i) qDebug() << qPrintable(QString(i + 1, QLatin1Char('-'))) << typeid(*nodes.at(i)).name(); } -#endif // DEBUG_AST_PATH +#endif // WITH_AST_PATH_DUMP bool ASTPath::preVisit(AST *ast) { diff --git a/src/libs/cplusplus/ASTPath.h b/src/libs/cplusplus/ASTPath.h index b74ec3f87c2e219f3eade2ac1222ccbb739e1fa0..1bf29d2dc2201bc5589494ec2d56aee277f57abd 100644 --- a/src/libs/cplusplus/ASTPath.h +++ b/src/libs/cplusplus/ASTPath.h @@ -38,7 +38,7 @@ #include <QList> #include <QTextCursor> -#undef DEBUG_AST_PATH +#undef WITH_AST_PATH_DUMP namespace CPlusPlus { @@ -56,7 +56,7 @@ public: /// line and column are 1-based! QList<AST *> operator()(int line, int column); -#ifdef DEBUG_AST_PATH +#ifdef WITH_AST_PATH_DUMP static void dump(const QList<AST *> nodes); #endif diff --git a/src/plugins/cpptools/builtinindexingsupport.cpp b/src/plugins/cpptools/builtinindexingsupport.cpp index 3def71feeb93d012206857b34d11aa5fbd004528..e2b4ffb8ee886d86f48b222a61a3d8598a449cee 100644 --- a/src/plugins/cpptools/builtinindexingsupport.cpp +++ b/src/plugins/cpptools/builtinindexingsupport.cpp @@ -1,6 +1,7 @@ #include "builtinindexingsupport.h" #include "cppmodelmanager.h" +#include "cpppreprocessor.h" #include "searchsymbols.h" #include "cpptoolsconstants.h" #include "cppprojectfile.h" diff --git a/src/plugins/cpptools/cppcompletionassist.cpp b/src/plugins/cpptools/cppcompletionassist.cpp index cae9114ef7e7449fe0e821bbadd0ac11a42c76af..e27761f3a929fe71a4faa7fe02dce360dbcc30ad 100644 --- a/src/plugins/cpptools/cppcompletionassist.cpp +++ b/src/plugins/cpptools/cppcompletionassist.cpp @@ -28,8 +28,8 @@ ****************************************************************************/ #include "cppcompletionassist.h" - #include "cppmodelmanager.h" +#include "cpptoolsconstants.h" #include "cppdoxygen.h" #include <coreplugin/icore.h> diff --git a/src/plugins/cpptools/cppmodelmanager.cpp b/src/plugins/cpptools/cppmodelmanager.cpp index d09ac81655bd0323be8e76c695d0d71208c40121..26bdd65322e612dd946040aae6cf25fdc2a44b70 100644 --- a/src/plugins/cpptools/cppmodelmanager.cpp +++ b/src/plugins/cpptools/cppmodelmanager.cpp @@ -28,6 +28,8 @@ ****************************************************************************/ #include "cppmodelmanager.h" +#include "cpppreprocessor.h" +#include "cpptoolsconstants.h" #include "builtinindexingsupport.h" #include "cppcompletionassist.h" @@ -44,7 +46,6 @@ #include <projectexplorer/session.h> #include <extensionsystem/pluginmanager.h> -#include <utils/hostosinfo.h> #include <utils/qtcassert.h> #include <QCoreApplication> @@ -53,9 +54,11 @@ #include <QTimer> #include <QTextBlock> -#include <functional> +#if defined(QTCREATOR_WITH_DUMP_AST) && defined(Q_CC_GNU) +#define WITH_AST_DUMP #include <iostream> #include <sstream> +#endif namespace CppTools { @@ -96,7 +99,7 @@ using namespace CppTools; using namespace CppTools::Internal; using namespace CPlusPlus; -#if defined(QTCREATOR_WITH_DUMP_AST) && defined(Q_CC_GNU) +#ifdef WITH_AST_DUMP #include <cxxabi.h> @@ -169,421 +172,6 @@ static const char pp_configuration[] = "#define __inline inline\n" "#define __forceinline inline\n"; -CppPreprocessor::CppPreprocessor(QPointer<CppModelManager> modelManager, bool dumpFileNameWhileParsing) - : m_snapshot(modelManager->snapshot()), - m_modelManager(modelManager), - m_dumpFileNameWhileParsing(dumpFileNameWhileParsing), - m_preprocess(this, &m_env), - m_revision(0) -{ - m_preprocess.setKeepComments(true); -} - -CppPreprocessor::~CppPreprocessor() -{ } - -void CppPreprocessor::setRevision(unsigned revision) -{ m_revision = revision; } - -void CppPreprocessor::setWorkingCopy(const CppModelManagerInterface::WorkingCopy &workingCopy) -{ m_workingCopy = workingCopy; } - -void CppPreprocessor::setIncludePaths(const QStringList &includePaths) -{ - m_includePaths.clear(); - - for (int i = 0; i < includePaths.size(); ++i) { - const QString &path = includePaths.at(i); - - if (Utils::HostOsInfo::isMacHost()) { - if (i + 1 < includePaths.size() && path.endsWith(QLatin1String(".framework/Headers"))) { - const QFileInfo pathInfo(path); - const QFileInfo frameworkFileInfo(pathInfo.path()); - const QString frameworkName = frameworkFileInfo.baseName(); - - const QFileInfo nextIncludePath = includePaths.at(i + 1); - if (nextIncludePath.fileName() == frameworkName) { - // We got a QtXXX.framework/Headers followed by $QTDIR/include/QtXXX. - // In this case we prefer to include files from $QTDIR/include/QtXXX. - continue; - } - } - } - m_includePaths.append(cleanPath(path)); - } -} - -void CppPreprocessor::setFrameworkPaths(const QStringList &frameworkPaths) -{ - m_frameworkPaths.clear(); - - foreach (const QString &frameworkPath, frameworkPaths) { - addFrameworkPath(frameworkPath); - } -} - -// Add the given framework path, and expand private frameworks. -// -// Example: -// <framework-path>/ApplicationServices.framework -// has private frameworks in: -// <framework-path>/ApplicationServices.framework/Frameworks -// if the "Frameworks" folder exists inside the top level framework. -void CppPreprocessor::addFrameworkPath(const QString &frameworkPath) -{ - // The algorithm below is a bit too eager, but that's because we're not getting - // in the frameworks we're linking against. If we would have that, then we could - // add only those private frameworks. - QString cleanFrameworkPath = cleanPath(frameworkPath); - if (!m_frameworkPaths.contains(cleanFrameworkPath)) - m_frameworkPaths.append(cleanFrameworkPath); - - const QDir frameworkDir(cleanFrameworkPath); - const QStringList filter = QStringList() << QLatin1String("*.framework"); - foreach (const QFileInfo &framework, frameworkDir.entryInfoList(filter)) { - if (!framework.isDir()) - continue; - const QFileInfo privateFrameworks(framework.absoluteFilePath(), QLatin1String("Frameworks")); - if (privateFrameworks.exists() && privateFrameworks.isDir()) - addFrameworkPath(privateFrameworks.absoluteFilePath()); - } -} - -void CppPreprocessor::setTodo(const QStringList &files) -{ m_todo = QSet<QString>::fromList(files); } - -namespace { -class Process: public std::unary_function<Document::Ptr, void> -{ - QPointer<CppModelManager> _modelManager; - Document::Ptr _doc; - Document::CheckMode _mode; - -public: - Process(QPointer<CppModelManager> modelManager, - Document::Ptr doc, - const CppModelManager::WorkingCopy &workingCopy) - : _modelManager(modelManager), - _doc(doc), - _mode(Document::FastCheck) - { - - if (workingCopy.contains(_doc->fileName())) - _mode = Document::FullCheck; - } - - void operator()() - { - _doc->check(_mode); - - if (_modelManager) - _modelManager->emitDocumentUpdated(_doc); - - _doc->releaseSourceAndAST(); - } -}; -} // end of anonymous namespace - -void CppPreprocessor::run(const QString &fileName) -{ - sourceNeeded(0, fileName, IncludeGlobal); -} - -void CppPreprocessor::removeFromCache(const QString &fileName) -{ - m_snapshot.remove(fileName); -} - -void CppPreprocessor::resetEnvironment() -{ - m_env.reset(); - m_processed.clear(); -} - -void CppPreprocessor::getFileContents(const QString &absoluteFilePath, - QString *contents, - unsigned *revision) const -{ - if (absoluteFilePath.isEmpty()) - return; - - if (m_workingCopy.contains(absoluteFilePath)) { - QPair<QString, unsigned> entry = m_workingCopy.get(absoluteFilePath); - if (contents) - *contents = entry.first; - if (revision) - *revision = entry.second; - return; - } - - QFile file(absoluteFilePath); - if (file.open(QFile::ReadOnly | QFile::Text)) { - QTextCodec *defaultCodec = Core::EditorManager::instance()->defaultTextCodec(); - QTextStream stream(&file); - stream.setCodec(defaultCodec); - if (contents) - *contents = stream.readAll(); - if (revision) - *revision = 0; - file.close(); - } -} - -bool CppPreprocessor::checkFile(const QString &absoluteFilePath) const -{ - if (absoluteFilePath.isEmpty() || m_included.contains(absoluteFilePath)) - return true; - - QFileInfo fileInfo(absoluteFilePath); - return fileInfo.isFile() && fileInfo.isReadable(); -} - -/// Resolve the given file name to its absolute path w.r.t. the include type. -QString CppPreprocessor::resolveFile(const QString &fileName, IncludeType type) -{ - if (type == IncludeGlobal) { - QHash<QString, QString>::ConstIterator it = m_fileNameCache.find(fileName); - if (it != m_fileNameCache.end()) - return it.value(); - const QString fn = resolveFile_helper(fileName, type); - m_fileNameCache.insert(fileName, fn); - return fn; - } - - // IncludeLocal, IncludeNext - return resolveFile_helper(fileName, type); -} - -QString CppPreprocessor::cleanPath(const QString &path) -{ - QString result = QDir::cleanPath(path); - const QChar slash(QLatin1Char('/')); - if (!result.endsWith(slash)) - result.append(slash); - return result; -} - -QString CppPreprocessor::resolveFile_helper(const QString &fileName, IncludeType type) -{ - QFileInfo fileInfo(fileName); - if (fileName == Preprocessor::configurationFileName || fileInfo.isAbsolute()) - return fileName; - - if (type == IncludeLocal && m_currentDoc) { - QFileInfo currentFileInfo(m_currentDoc->fileName()); - QString path = cleanPath(currentFileInfo.absolutePath()) + fileName; - if (checkFile(path)) - return path; - } - - foreach (const QString &includePath, m_includePaths) { - QString path = includePath + fileName; - if (checkFile(path)) - return path; - } - - int index = fileName.indexOf(QLatin1Char('/')); - if (index != -1) { - QString frameworkName = fileName.left(index); - QString name = frameworkName + QLatin1String(".framework/Headers/") + fileName.mid(index + 1); - - foreach (const QString &frameworkPath, m_frameworkPaths) { - QString path = frameworkPath + name; - if (checkFile(path)) - return path; - } - } - - //qDebug() << "**** file" << fileName << "not found!"; - return QString(); -} - -void CppPreprocessor::macroAdded(const Macro ¯o) -{ - if (! m_currentDoc) - return; - - m_currentDoc->appendMacro(macro); -} - -static inline const Macro revision(const CppModelManagerInterface::WorkingCopy &s, const Macro ¯o) -{ - Macro newMacro(macro); - newMacro.setFileRevision(s.get(macro.fileName()).second); - return newMacro; -} - -void CppPreprocessor::passedMacroDefinitionCheck(unsigned offset, unsigned line, const Macro ¯o) -{ - if (! m_currentDoc) - return; - - m_currentDoc->addMacroUse(revision(m_workingCopy, macro), offset, macro.name().length(), line, - QVector<MacroArgumentReference>()); -} - -void CppPreprocessor::failedMacroDefinitionCheck(unsigned offset, const ByteArrayRef &name) -{ - if (! m_currentDoc) - return; - - m_currentDoc->addUndefinedMacroUse(QByteArray(name.start(), name.size()), offset); -} - -void CppPreprocessor::notifyMacroReference(unsigned offset, unsigned line, const Macro ¯o) -{ - if (! m_currentDoc) - return; - - m_currentDoc->addMacroUse(revision(m_workingCopy, macro), offset, macro.name().length(), line, - QVector<MacroArgumentReference>()); -} - -void CppPreprocessor::startExpandingMacro(unsigned offset, unsigned line, - const Macro ¯o, - const QVector<MacroArgumentReference> &actuals) -{ - if (! m_currentDoc) - return; - - m_currentDoc->addMacroUse(revision(m_workingCopy, macro), offset, macro.name().length(), line, actuals); -} - -void CppPreprocessor::stopExpandingMacro(unsigned, const Macro &) -{ - if (! m_currentDoc) - return; - - //qDebug() << "stop expanding:" << macro.name; -} - -void CppPreprocessor::markAsIncludeGuard(const QByteArray ¯oName) -{ - if (!m_currentDoc) - return; - - m_currentDoc->setIncludeGuardMacroName(macroName); -} - -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 = m_snapshot.document(includedFile)) - mergeEnvironment(includedDoc); - else - run(includedFile); - } - - m_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(unsigned line, const QString &fileName, IncludeType type) -{ - if (fileName.isEmpty()) - return; - - QString absoluteFileName = resolveFile(fileName, type); - absoluteFileName = QDir::cleanPath(absoluteFileName); - if (m_currentDoc && !absoluteFileName.isEmpty()) - m_currentDoc->addIncludeFile(absoluteFileName, line); - if (m_included.contains(absoluteFileName)) - return; // we've already seen this file. - if (absoluteFileName != modelManager()->configurationFileName()) - m_included.insert(absoluteFileName); - - unsigned editorRevision = 0; - QString contents; - getFileContents(absoluteFileName, &contents, &editorRevision); - if (m_currentDoc) { - if (contents.isEmpty() && ! QFileInfo(absoluteFileName).isAbsolute()) { - QString msg = QCoreApplication::translate( - "CppPreprocessor", "%1: No such file or directory").arg(fileName); - - Document::DiagnosticMessage d(Document::DiagnosticMessage::Warning, - m_currentDoc->fileName(), - line, /*column = */ 0, - msg); - - m_currentDoc->addDiagnosticMessage(d); - - //qWarning() << "file not found:" << fileName << m_currentDoc->fileName() << env.current_line; - - return; - } - } - - if (m_dumpFileNameWhileParsing) { - qDebug() << "Parsing file:" << absoluteFileName -// << "contents:" << contents.size() - ; - } - - Document::Ptr doc = m_snapshot.document(absoluteFileName); - if (doc) { - mergeEnvironment(doc); - return; - } - - doc = Document::create(absoluteFileName); - doc->setRevision(m_revision); - doc->setEditorRevision(editorRevision); - - QFileInfo info(absoluteFileName); - if (info.exists()) - doc->setLastModified(info.lastModified()); - - Document::Ptr previousDoc = switchDocument(doc); - - 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()); } - - doc->setUtf8Source(preprocessedCode); - doc->keepSourceAndAST(); - doc->tokenize(); - - m_snapshot.insert(doc); - m_todo.remove(absoluteFileName); - - Process process(m_modelManager, doc, m_workingCopy); - process(); - - (void) switchDocument(previousDoc); -} - -Document::Ptr CppPreprocessor::switchDocument(Document::Ptr doc) -{ - Document::Ptr previousDoc = m_currentDoc; - m_currentDoc = doc; - return previousDoc; -} - void CppModelManager::updateModifiedSourceFiles() { const Snapshot snapshot = this->snapshot(); diff --git a/src/plugins/cpptools/cppmodelmanager.h b/src/plugins/cpptools/cppmodelmanager.h index d347b851e53c0a64f46929078a81772c5cab2c62..e78729958f498524f0c999b4ae4babd9958b3e71 100644 --- a/src/plugins/cpptools/cppmodelmanager.h +++ b/src/plugins/cpptools/cppmodelmanager.h @@ -31,15 +31,11 @@ #define CPPMODELMANAGER_H #include "cpptools_global.h" -#include "cpptoolsconstants.h" #include "cppmodelmanagerinterface.h" #include <projectexplorer/project.h> #include <texteditor/basetexteditor.h> -#include <cplusplus/PreprocessorEnvironment.h> -#include <cplusplus/pp-engine.h> - #include <QHash> #include <QMutex> @@ -51,7 +47,6 @@ class BaseTextEditorWidget; namespace CppTools { -class CppCompletionSupportFactory; class CppEditorSupport; class CppHighlightingSupportFactory; @@ -205,77 +200,6 @@ private: CppIndexingSupport *m_internalIndexingSupport; }; -class CPPTOOLS_EXPORT CppPreprocessor: public CPlusPlus::Client -{ - Q_DISABLE_COPY(CppPreprocessor) - -public: - CppPreprocessor(QPointer<CppModelManager> modelManager, bool dumpFileNameWhileParsing = false); - virtual ~CppPreprocessor(); - - void setRevision(unsigned revision); - void setWorkingCopy(const CppTools::CppModelManagerInterface::WorkingCopy &workingCopy); - void setIncludePaths(const QStringList &includePaths); - void setFrameworkPaths(const QStringList &frameworkPaths); - void addFrameworkPath(const QString &frameworkPath); - void setProjectFiles(const QStringList &files); - void setTodo(const QStringList &files); - - void run(const QString &fileName); - void removeFromCache(const QString &fileName); - - void resetEnvironment(); - static QString cleanPath(const QString &path); - - const QSet<QString> &todo() const - { return m_todo; } - - CppModelManager *modelManager() const - { return m_modelManager.data(); } - -protected: - CPlusPlus::Document::Ptr switchDocument(CPlusPlus::Document::Ptr doc); - - void getFileContents(const QString &absoluteFilePath, QString *contents, unsigned *revision) const; - bool checkFile(const QString &absoluteFilePath) const; - QString resolveFile(const QString &fileName, IncludeType type); - QString resolveFile_helper(const QString &fileName, IncludeType type); - - void mergeEnvironment(CPlusPlus::Document::Ptr doc); - - virtual void macroAdded(const CPlusPlus::Macro ¯o); - virtual void passedMacroDefinitionCheck(unsigned offset, unsigned line, - const CPlusPlus::Macro ¯o); - virtual void failedMacroDefinitionCheck(unsigned offset, const CPlusPlus::ByteArrayRef &name); - virtual void notifyMacroReference(unsigned offset, unsigned line, - const CPlusPlus::Macro ¯o); - virtual void startExpandingMacro(unsigned offset, - unsigned line, - const CPlusPlus::Macro ¯o, - const QVector<CPlusPlus::MacroArgumentReference> &actuals); - virtual void stopExpandingMacro(unsigned offset, const CPlusPlus::Macro ¯o); - virtual void markAsIncludeGuard(const QByteArray ¯oName); - virtual void startSkippingBlocks(unsigned offset); - virtual void stopSkippingBlocks(unsigned offset); - virtual void sourceNeeded(unsigned line, const QString &fileName, IncludeType type); - -private: - CPlusPlus::Snapshot m_snapshot; - QPointer<CppModelManager> m_modelManager; - bool m_dumpFileNameWhileParsing; - CPlusPlus::Environment m_env; - CPlusPlus::Preprocessor m_preprocess; - QStringList m_includePaths; - CppTools::CppModelManagerInterface::WorkingCopy m_workingCopy; - QStringList m_frameworkPaths; - QSet<QString> m_included; - CPlusPlus::Document::Ptr m_currentDoc; - QSet<QString> m_todo; - QSet<QString> m_processed; - unsigned m_revision; - QHash<QString, QString> m_fileNameCache; -}; - } // namespace Internal } // namespace CppTools diff --git a/src/plugins/cpptools/cppmodelmanager_test.cpp b/src/plugins/cpptools/cppmodelmanager_test.cpp index 2a9462674f4bdeb5944e62021d0a08401d92eb8e..3385768c1526850c4392af6e6df42ab0d275e43d 100644 --- a/src/plugins/cpptools/cppmodelmanager_test.cpp +++ b/src/plugins/cpptools/cppmodelmanager_test.cpp @@ -29,7 +29,7 @@ #include "cpptoolsplugin.h" -#include "cppmodelmanager.h" +#include "cpppreprocessor.h" #include "modelmanagertesthelper.h" #include <QtTest> diff --git a/src/plugins/cpptools/cpppreprocessor.cpp b/src/plugins/cpptools/cpppreprocessor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5ab1c9a04e713138ab24299bb41e01eb8ae89d30 --- /dev/null +++ b/src/plugins/cpptools/cpppreprocessor.cpp @@ -0,0 +1,425 @@ +#include "cppmodelmanager.h" +#include "cpppreprocessor.h" + +#include <utils/hostosinfo.h> + +#include <QCoreApplication> + +using namespace CPlusPlus; +using namespace CppTools; +using namespace CppTools::Internal; + +CppPreprocessor::CppPreprocessor(QPointer<CppModelManager> modelManager, bool dumpFileNameWhileParsing) + : m_snapshot(modelManager->snapshot()), + m_modelManager(modelManager), + m_dumpFileNameWhileParsing(dumpFileNameWhileParsing), + m_preprocess(this, &m_env), + m_revision(0) +{ + m_preprocess.setKeepComments(true); +} + +CppPreprocessor::~CppPreprocessor() +{ } + +void CppPreprocessor::setRevision(unsigned revision) +{ m_revision = revision; } + +void CppPreprocessor::setWorkingCopy(const CppModelManagerInterface::WorkingCopy &workingCopy) +{ m_workingCopy = workingCopy; } + +void CppPreprocessor::setIncludePaths(const QStringList &includePaths) +{ + m_includePaths.clear(); + + for (int i = 0; i < includePaths.size(); ++i) { + const QString &path = includePaths.at(i); + + if (Utils::HostOsInfo::isMacHost()) { + if (i + 1 < includePaths.size() && path.endsWith(QLatin1String(".framework/Headers"))) { + const QFileInfo pathInfo(path); + const QFileInfo frameworkFileInfo(pathInfo.path()); + const QString frameworkName = frameworkFileInfo.baseName(); + + const QFileInfo nextIncludePath = includePaths.at(i + 1); + if (nextIncludePath.fileName() == frameworkName) { + // We got a QtXXX.framework/Headers followed by $QTDIR/include/QtXXX. + // In this case we prefer to include files from $QTDIR/include/QtXXX. + continue; + } + } + } + m_includePaths.append(cleanPath(path)); + } +} + +void CppPreprocessor::setFrameworkPaths(const QStringList &frameworkPaths) +{ + m_frameworkPaths.clear(); + + foreach (const QString &frameworkPath, frameworkPaths) { + addFrameworkPath(frameworkPath); + } +} + +// Add the given framework path, and expand private frameworks. +// +// Example: +// <framework-path>/ApplicationServices.framework +// has private frameworks in: +// <framework-path>/ApplicationServices.framework/Frameworks +// if the "Frameworks" folder exists inside the top level framework. +void CppPreprocessor::addFrameworkPath(const QString &frameworkPath) +{ + // The algorithm below is a bit too eager, but that's because we're not getting + // in the frameworks we're linking against. If we would have that, then we could + // add only those private frameworks. + QString cleanFrameworkPath = cleanPath(frameworkPath); + if (!m_frameworkPaths.contains(cleanFrameworkPath)) + m_frameworkPaths.append(cleanFrameworkPath); + + const QDir frameworkDir(cleanFrameworkPath); + const QStringList filter = QStringList() << QLatin1String("*.framework"); + foreach (const QFileInfo &framework, frameworkDir.entryInfoList(filter)) { + if (!framework.isDir()) + continue; + const QFileInfo privateFrameworks(framework.absoluteFilePath(), QLatin1String("Frameworks")); + if (privateFrameworks.exists() && privateFrameworks.isDir()) + addFrameworkPath(privateFrameworks.absoluteFilePath()); + } +} + +void CppPreprocessor::setTodo(const QStringList &files) +{ m_todo = QSet<QString>::fromList(files); } + +namespace { +class Process: public std::unary_function<Document::Ptr, void> +{ + QPointer<CppModelManager> _modelManager; + Document::Ptr _doc; + Document::CheckMode _mode; + +public: + Process(QPointer<CppModelManager> modelManager, + Document::Ptr doc, + const CppModelManager::WorkingCopy &workingCopy) + : _modelManager(modelManager), + _doc(doc), + _mode(Document::FastCheck) + { + + if (workingCopy.contains(_doc->fileName())) + _mode = Document::FullCheck; + } + + void operator()() + { + _doc->check(_mode); + + if (_modelManager) + _modelManager->emitDocumentUpdated(_doc); + + _doc->releaseSourceAndAST(); + } +}; +} // end of anonymous namespace + +void CppPreprocessor::run(const QString &fileName) +{ + sourceNeeded(0, fileName, IncludeGlobal); +} + +void CppPreprocessor::removeFromCache(const QString &fileName) +{ + m_snapshot.remove(fileName); +} + +void CppPreprocessor::resetEnvironment() +{ + m_env.reset(); + m_processed.clear(); +} + +void CppPreprocessor::getFileContents(const QString &absoluteFilePath, + QString *contents, + unsigned *revision) const +{ + if (absoluteFilePath.isEmpty()) + return; + + if (m_workingCopy.contains(absoluteFilePath)) { + QPair<QString, unsigned> entry = m_workingCopy.get(absoluteFilePath); + if (contents) + *contents = entry.first; + if (revision) + *revision = entry.second; + return; + } + + QFile file(absoluteFilePath); + if (file.open(QFile::ReadOnly | QFile::Text)) { + QTextCodec *defaultCodec = Core::EditorManager::instance()->defaultTextCodec(); + QTextStream stream(&file); + stream.setCodec(defaultCodec); + if (contents) + *contents = stream.readAll(); + if (revision) + *revision = 0; + file.close(); + } +} + +bool CppPreprocessor::checkFile(const QString &absoluteFilePath) const +{ + if (absoluteFilePath.isEmpty() || m_included.contains(absoluteFilePath)) + return true; + + QFileInfo fileInfo(absoluteFilePath); + return fileInfo.isFile() && fileInfo.isReadable(); +} + +/// Resolve the given file name to its absolute path w.r.t. the include type. +QString CppPreprocessor::resolveFile(const QString &fileName, IncludeType type) +{ + if (type == IncludeGlobal) { + QHash<QString, QString>::ConstIterator it = m_fileNameCache.find(fileName); + if (it != m_fileNameCache.end()) + return it.value(); + const QString fn = resolveFile_helper(fileName, type); + m_fileNameCache.insert(fileName, fn); + return fn; + } + + // IncludeLocal, IncludeNext + return resolveFile_helper(fileName, type); +} + +QString CppPreprocessor::cleanPath(const QString &path) +{ + QString result = QDir::cleanPath(path); + const QChar slash(QLatin1Char('/')); + if (!result.endsWith(slash)) + result.append(slash); + return result; +} + +QString CppPreprocessor::resolveFile_helper(const QString &fileName, IncludeType type) +{ + QFileInfo fileInfo(fileName); + if (fileName == Preprocessor::configurationFileName || fileInfo.isAbsolute()) + return fileName; + + if (type == IncludeLocal && m_currentDoc) { + QFileInfo currentFileInfo(m_currentDoc->fileName()); + QString path = cleanPath(currentFileInfo.absolutePath()) + fileName; + if (checkFile(path)) + return path; + } + + foreach (const QString &includePath, m_includePaths) { + QString path = includePath + fileName; + if (checkFile(path)) + return path; + } + + int index = fileName.indexOf(QLatin1Char('/')); + if (index != -1) { + QString frameworkName = fileName.left(index); + QString name = frameworkName + QLatin1String(".framework/Headers/") + fileName.mid(index + 1); + + foreach (const QString &frameworkPath, m_frameworkPaths) { + QString path = frameworkPath + name; + if (checkFile(path)) + return path; + } + } + + //qDebug() << "**** file" << fileName << "not found!"; + return QString(); +} + +void CppPreprocessor::macroAdded(const Macro ¯o) +{ + if (! m_currentDoc) + return; + + m_currentDoc->appendMacro(macro); +} + +static inline const Macro revision(const CppModelManagerInterface::WorkingCopy &s, const Macro ¯o) +{ + Macro newMacro(macro); + newMacro.setFileRevision(s.get(macro.fileName()).second); + return newMacro; +} + +void CppPreprocessor::passedMacroDefinitionCheck(unsigned offset, unsigned line, const Macro ¯o) +{ + if (! m_currentDoc) + return; + + m_currentDoc->addMacroUse(revision(m_workingCopy, macro), offset, macro.name().length(), line, + QVector<MacroArgumentReference>()); +} + +void CppPreprocessor::failedMacroDefinitionCheck(unsigned offset, const ByteArrayRef &name) +{ + if (! m_currentDoc) + return; + + m_currentDoc->addUndefinedMacroUse(QByteArray(name.start(), name.size()), offset); +} + +void CppPreprocessor::notifyMacroReference(unsigned offset, unsigned line, const Macro ¯o) +{ + if (! m_currentDoc) + return; + + m_currentDoc->addMacroUse(revision(m_workingCopy, macro), offset, macro.name().length(), line, + QVector<MacroArgumentReference>()); +} + +void CppPreprocessor::startExpandingMacro(unsigned offset, unsigned line, + const Macro ¯o, + const QVector<MacroArgumentReference> &actuals) +{ + if (! m_currentDoc) + return; + + m_currentDoc->addMacroUse(revision(m_workingCopy, macro), offset, macro.name().length(), line, actuals); +} + +void CppPreprocessor::stopExpandingMacro(unsigned, const Macro &) +{ + if (! m_currentDoc) + return; + + //qDebug() << "stop expanding:" << macro.name; +} + +void CppPreprocessor::markAsIncludeGuard(const QByteArray ¯oName) +{ + if (!m_currentDoc) + return; + + m_currentDoc->setIncludeGuardMacroName(macroName); +} + +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 = m_snapshot.document(includedFile)) + mergeEnvironment(includedDoc); + else + run(includedFile); + } + + m_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(unsigned line, const QString &fileName, IncludeType type) +{ + if (fileName.isEmpty()) + return; + + QString absoluteFileName = resolveFile(fileName, type); + absoluteFileName = QDir::cleanPath(absoluteFileName); + if (m_currentDoc && !absoluteFileName.isEmpty()) + m_currentDoc->addIncludeFile(absoluteFileName, line); + if (m_included.contains(absoluteFileName)) + return; // we've already seen this file. + if (absoluteFileName != modelManager()->configurationFileName()) + m_included.insert(absoluteFileName); + + unsigned editorRevision = 0; + QString contents; + getFileContents(absoluteFileName, &contents, &editorRevision); + if (m_currentDoc) { + if (contents.isEmpty() && ! QFileInfo(absoluteFileName).isAbsolute()) { + QString msg = QCoreApplication::translate( + "CppPreprocessor", "%1: No such file or directory").arg(fileName); + + Document::DiagnosticMessage d(Document::DiagnosticMessage::Warning, + m_currentDoc->fileName(), + line, /*column = */ 0, + msg); + + m_currentDoc->addDiagnosticMessage(d); + + //qWarning() << "file not found:" << fileName << m_currentDoc->fileName() << env.current_line; + + return; + } + } + + if (m_dumpFileNameWhileParsing) { + qDebug() << "Parsing file:" << absoluteFileName +// << "contents:" << contents.size() + ; + } + + Document::Ptr doc = m_snapshot.document(absoluteFileName); + if (doc) { + mergeEnvironment(doc); + return; + } + + doc = Document::create(absoluteFileName); + doc->setRevision(m_revision); + doc->setEditorRevision(editorRevision); + + QFileInfo info(absoluteFileName); + if (info.exists()) + doc->setLastModified(info.lastModified()); + + Document::Ptr previousDoc = switchDocument(doc); + + 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()); } + + doc->setUtf8Source(preprocessedCode); + doc->keepSourceAndAST(); + doc->tokenize(); + + m_snapshot.insert(doc); + m_todo.remove(absoluteFileName); + + Process process(m_modelManager, doc, m_workingCopy); + process(); + + (void) switchDocument(previousDoc); +} + +Document::Ptr CppPreprocessor::switchDocument(Document::Ptr doc) +{ + Document::Ptr previousDoc = m_currentDoc; + m_currentDoc = doc; + return previousDoc; +} diff --git a/src/plugins/cpptools/cpppreprocessor.h b/src/plugins/cpptools/cpppreprocessor.h new file mode 100644 index 0000000000000000000000000000000000000000..d931a880c4b7ae3af5233b54985b152b84e14618 --- /dev/null +++ b/src/plugins/cpptools/cpppreprocessor.h @@ -0,0 +1,91 @@ +#ifndef CPPPREPROCESSOR_H +#define CPPPREPROCESSOR_H + +#include "cppmodelmanagerinterface.h" + +#include <cplusplus/PreprocessorEnvironment.h> +#include <cplusplus/pp-engine.h> + +#include <QHash> +#include <QPointer> + +namespace CppTools { +namespace Internal { + +class CppModelManager; + +class CPPTOOLS_EXPORT CppPreprocessor: public CPlusPlus::Client +{ + Q_DISABLE_COPY(CppPreprocessor) + +public: + CppPreprocessor(QPointer<CppModelManager> modelManager, bool dumpFileNameWhileParsing = false); + virtual ~CppPreprocessor(); + + void setRevision(unsigned revision); + void setWorkingCopy(const CppTools::CppModelManagerInterface::WorkingCopy &workingCopy); + void setIncludePaths(const QStringList &includePaths); + void setFrameworkPaths(const QStringList &frameworkPaths); + void addFrameworkPath(const QString &frameworkPath); + void setProjectFiles(const QStringList &files); + void setTodo(const QStringList &files); + + void run(const QString &fileName); + void removeFromCache(const QString &fileName); + + void resetEnvironment(); + static QString cleanPath(const QString &path); + + const QSet<QString> &todo() const + { return m_todo; } + + CppModelManager *modelManager() const + { return m_modelManager.data(); } + +protected: + CPlusPlus::Document::Ptr switchDocument(CPlusPlus::Document::Ptr doc); + + void getFileContents(const QString &absoluteFilePath, QString *contents, unsigned *revision) const; + bool checkFile(const QString &absoluteFilePath) const; + QString resolveFile(const QString &fileName, IncludeType type); + QString resolveFile_helper(const QString &fileName, IncludeType type); + + void mergeEnvironment(CPlusPlus::Document::Ptr doc); + + virtual void macroAdded(const CPlusPlus::Macro ¯o); + virtual void passedMacroDefinitionCheck(unsigned offset, unsigned line, + const CPlusPlus::Macro ¯o); + virtual void failedMacroDefinitionCheck(unsigned offset, const CPlusPlus::ByteArrayRef &name); + virtual void notifyMacroReference(unsigned offset, unsigned line, + const CPlusPlus::Macro ¯o); + virtual void startExpandingMacro(unsigned offset, + unsigned line, + const CPlusPlus::Macro ¯o, + const QVector<CPlusPlus::MacroArgumentReference> &actuals); + virtual void stopExpandingMacro(unsigned offset, const CPlusPlus::Macro ¯o); + virtual void markAsIncludeGuard(const QByteArray ¯oName); + virtual void startSkippingBlocks(unsigned offset); + virtual void stopSkippingBlocks(unsigned offset); + virtual void sourceNeeded(unsigned line, const QString &fileName, IncludeType type); + +private: + CPlusPlus::Snapshot m_snapshot; + QPointer<CppModelManager> m_modelManager; + bool m_dumpFileNameWhileParsing; + CPlusPlus::Environment m_env; + CPlusPlus::Preprocessor m_preprocess; + QStringList m_includePaths; + CppTools::CppModelManagerInterface::WorkingCopy m_workingCopy; + QStringList m_frameworkPaths; + QSet<QString> m_included; + CPlusPlus::Document::Ptr m_currentDoc; + QSet<QString> m_todo; + QSet<QString> m_processed; + unsigned m_revision; + QHash<QString, QString> m_fileNameCache; +}; + +} // namespace Internal +} // namespace CppTools + +#endif // CPPPREPROCESSOR_H diff --git a/src/plugins/cpptools/cpptools.pro b/src/plugins/cpptools/cpptools.pro index f77e869d68e56cc33282801d4746098c66abfeb7..efeff55f250509fa6a80bd491fd99d7ef9bcea04 100644 --- a/src/plugins/cpptools/cpptools.pro +++ b/src/plugins/cpptools/cpptools.pro @@ -44,7 +44,8 @@ HEADERS += completionsettingspage.h \ cppindexingsupport.h \ builtinindexingsupport.h \ cpppointerdeclarationformatter.h \ - cppprojectfile.h + cppprojectfile.h \ + cpppreprocessor.h SOURCES += completionsettingspage.cpp \ cppclassesfilter.cpp \ @@ -87,7 +88,8 @@ SOURCES += completionsettingspage.cpp \ cppindexingsupport.cpp \ builtinindexingsupport.cpp \ cpppointerdeclarationformatter.cpp \ - cppprojectfile.cpp + cppprojectfile.cpp \ + cpppreprocessor.cpp FORMS += completionsettingspage.ui \ cppfilesettingspage.ui \ diff --git a/src/plugins/cpptools/cpptools.qbs b/src/plugins/cpptools/cpptools.qbs index a6a98539b8d4198920cbf46556010c2a7626103c..059a3dfc7e2525a8799fec7e79593c06bd97f9b2 100644 --- a/src/plugins/cpptools/cpptools.qbs +++ b/src/plugins/cpptools/cpptools.qbs @@ -105,7 +105,9 @@ QtcPlugin { "uicodecompletionsupport.cpp", "uicodecompletionsupport.h", "builtinindexingsupport.cpp", - "builtinindexingsupport.h" + "builtinindexingsupport.h", + "cpppreprocessor.cpp", + "cpppreprocessor.h" ] Group { diff --git a/src/plugins/cpptools/cpptoolsplugin.cpp b/src/plugins/cpptools/cpptoolsplugin.cpp index e8f1a46c84727a6a638dfdb10cd27003e198223f..7f70a37640c9cfb21620743049bfeba69d9c3fe5 100644 --- a/src/plugins/cpptools/cpptoolsplugin.cpp +++ b/src/plugins/cpptools/cpptoolsplugin.cpp @@ -27,8 +27,8 @@ ** ****************************************************************************/ +#include "cpptoolsconstants.h" #include "cpptoolsplugin.h" - #include "cppfilesettingspage.h" #include "cppcodestylesettingspage.h" #include "cppclassesfilter.h" diff --git a/src/plugins/cpptools/symbolsfindfilter.cpp b/src/plugins/cpptools/symbolsfindfilter.cpp index fe4fc332c98657a445793322e105504cbd89f4cb..78c636674d0cf550a88b42eaf0555b43ed00962c 100644 --- a/src/plugins/cpptools/symbolsfindfilter.cpp +++ b/src/plugins/cpptools/symbolsfindfilter.cpp @@ -28,8 +28,8 @@ ****************************************************************************/ #include "symbolsfindfilter.h" - #include "cppmodelmanager.h" +#include "cpptoolsconstants.h" #include <coreplugin/icore.h> #include <coreplugin/progressmanager/futureprogress.h>