Files
qt-creator/src/plugins/cpptools/cppmodelmanager.cpp

1425 lines
43 KiB
C++
Raw Normal View History

/****************************************************************************
2008-12-02 12:01:29 +01:00
**
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
2008-12-02 12:01:29 +01:00
**
** This file is part of Qt Creator.
2008-12-02 12:01:29 +01:00
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
2010-12-17 16:01:08 +01:00
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
2008-12-02 15:08:31 +01:00
2008-12-08 12:59:33 +01:00
#include <cplusplus/pp.h>
#include <cplusplus/Overview.h>
2008-12-02 12:01:29 +01:00
#include "cppmodelmanager.h"
#include "cppcompletionassist.h"
#include "cpphighlightingsupport.h"
#include "cpphighlightingsupportinternal.h"
#include "abstracteditorsupport.h"
#include "cpptoolsconstants.h"
#include "cpptoolseditorsupport.h"
#include "cppfindreferences.h"
2008-12-02 12:01:29 +01:00
#include <functional>
#include <QtConcurrentRun>
#include <QFutureSynchronizer>
#include <utils/runextensions.h>
#include <texteditor/itexteditor.h>
#include <texteditor/basetexteditor.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/session.h>
#include <coreplugin/icore.h>
#include <coreplugin/mimedatabase.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <extensionsystem/pluginmanager.h>
#include <utils/hostosinfo.h>
2008-12-09 15:25:01 +01:00
#include <utils/qtcassert.h>
2008-12-02 12:01:29 +01:00
#include <TranslationUnit.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>
2008-12-02 12:01:29 +01:00
#include <Lexer.h>
#include <Token.h>
#include <Parser.h>
#include <Control.h>
#include <CoreTypes.h>
2008-12-02 12:01:29 +01:00
#include <QCoreApplication>
#include <QDebug>
#include <QMutexLocker>
#include <QTime>
#include <QTimer>
#include <QtConcurrentMap>
#include <QTextBlock>
#include <iostream>
#include <sstream>
2008-12-09 15:25:01 +01:00
namespace CPlusPlus {
uint qHash(const CppModelManagerInterface::ProjectPart &p)
{
uint h = qHash(p.defines) ^ p.language ^ ((int) p.cxx11Enabled);
foreach (const QString &i, p.includePaths)
h ^= qHash(i);
foreach (const QString &f, p.frameworkPaths)
h ^= qHash(f);
return h;
}
bool operator==(const CppModelManagerInterface::ProjectPart &p1,
const CppModelManagerInterface::ProjectPart &p2)
{
if (p1.defines != p2.defines)
return false;
if (p1.language != p2.language)
return false;
if (p1.cxx11Enabled != p2.cxx11Enabled)
return false;
if (p1.includePaths != p2.includePaths)
return false;
return p1.frameworkPaths == p2.frameworkPaths;
}
} // namespace CPlusPlus
2008-12-08 11:08:48 +01:00
using namespace CppTools;
using namespace CppTools::Internal;
2008-12-02 12:01:29 +01:00
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; }
};
2009-03-05 09:46:54 +01:00
#endif // QTCREATOR_WITH_DUMP_AST
2008-12-02 12:01:29 +01:00
static const char pp_configuration_file[] = "<configuration>";
static const char pp_configuration[] =
"# 1 \"<configuration>\"\n"
"#define __cplusplus 1\n"
"#define __extension__\n"
"#define __context__\n"
"#define __range__\n"
"#define restrict\n"
"#define __restrict\n"
"#define __restrict__\n"
2008-12-02 12:01:29 +01:00
"#define __complex__\n"
"#define __imag__\n"
"#define __real__\n"
"#define __builtin_va_arg(a,b) ((b)0)\n"
2008-12-02 12:01:29 +01:00
// ### add macros for win32
"#define __cdecl\n"
"#define __stdcall\n"
2008-12-02 12:01:29 +01:00
"#define QT_WA(x) x\n"
"#define CALLBACK\n"
"#define STDMETHODCALLTYPE\n"
"#define __RPC_FAR\n"
"#define __declspec(a)\n"
"#define STDMETHOD(method) virtual HRESULT STDMETHODCALLTYPE method\n"
"#define __try try\n"
"#define __except catch\n"
"#define __finally\n"
"#define __inline inline\n"
"#define __forceinline inline\n";
2008-12-02 12:01:29 +01:00
CppPreprocessor::CppPreprocessor(QPointer<CppModelManager> modelManager, bool dumpFileNameWhileParsing)
: snapshot(modelManager->snapshot()),
m_modelManager(modelManager),
m_dumpFileNameWhileParsing(dumpFileNameWhileParsing),
preprocess(this, &env),
m_revision(0)
{
preprocess.setKeepComments(true);
}
2009-03-11 12:00:07 +01:00
CppPreprocessor::~CppPreprocessor()
2008-12-08 11:08:48 +01:00
{ }
2008-12-02 12:01:29 +01:00
void CppPreprocessor::setRevision(unsigned revision)
{ m_revision = revision; }
void CppPreprocessor::setWorkingCopy(const CppModelManagerInterface::WorkingCopy &workingCopy)
2008-12-08 11:08:48 +01:00
{ m_workingCopy = workingCopy; }
2008-12-02 12:01:29 +01:00
2008-12-08 11:08:48 +01:00
void CppPreprocessor::setIncludePaths(const QStringList &includePaths)
{
m_includePaths.clear();
for (int i = 0; i < includePaths.size(); ++i) {
2010-03-17 14:49:42 +01:00
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(path);
} else {
m_includePaths.append(path);
}
}
}
2008-12-08 11:08:48 +01:00
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.
if (!m_frameworkPaths.contains(frameworkPath)) {
m_frameworkPaths.append(frameworkPath);
}
const QDir frameworkDir(frameworkPath);
const QStringList filter = QStringList() << QLatin1String("*.framework");
foreach (const QFileInfo &framework, frameworkDir.entryInfoList(filter)) {
if (!framework.isDir())
continue;
const QFileInfo privateFrameworks(framework.absoluteFilePath(), QLatin1String("Frameworks"));
if (privateFrameworks.exists() && privateFrameworks.isDir()) {
addFrameworkPath(privateFrameworks.absoluteFilePath());
}
}
}
2008-12-02 12:01:29 +01:00
2008-12-08 11:08:48 +01:00
void CppPreprocessor::setProjectFiles(const QStringList &files)
{ m_projectFiles = files; }
2009-02-23 15:57:37 +01:00
void CppPreprocessor::setTodo(const QStringList &files)
{ m_todo = QSet<QString>::fromList(files); }
2010-02-03 13:21:08 +10:00
namespace {
class Process: public std::unary_function<Document::Ptr, void>
{
QPointer<CppModelManager> _modelManager;
2009-06-02 16:50:43 +02:00
Snapshot _snapshot;
Document::Ptr _doc;
2010-05-10 10:19:37 +02:00
Document::CheckMode _mode;
public:
2009-06-02 16:50:43 +02:00
Process(QPointer<CppModelManager> modelManager,
2010-05-10 10:19:37 +02:00
Document::Ptr doc,
const Snapshot &snapshot,
const CppModelManager::WorkingCopy &workingCopy)
2009-06-02 16:50:43 +02:00
: _modelManager(modelManager),
_snapshot(snapshot),
2010-05-10 10:19:37 +02:00
_doc(doc),
_mode(Document::FastCheck)
2009-06-02 16:50:43 +02:00
{
2009-06-26 09:11:14 +02:00
2010-05-10 10:19:37 +02:00
if (workingCopy.contains(_doc->fileName()))
_mode = Document::FullCheck;
}
2009-06-26 09:11:14 +02:00
2010-05-10 10:19:37 +02:00
void operator()()
{
_doc->check(_mode);
2010-09-02 12:50:37 +02:00
if (_modelManager)
_modelManager->emitDocumentUpdated(_doc);
_doc->releaseSourceAndAST();
}
};
} // end of anonymous namespace
void CppPreprocessor::run(const QString &fileName)
{
QString absoluteFilePath = fileName;
C++: Core changes in preprocessing Summary of most relevant items: - Preprocessor output format change. No more gen true/false. Instead a more intuitive and natural expansion (like from a real compiler) is performed directly corresponding to the macro invocation. Notice that information about the generated tokens is not lost, because it's now embedded in the expansion section header (in terms of lines and columns as explained in the code). In addition the location on where the macro expansion happens is also documented for future use. - Fix line control directives and associated token line numbers. This was not detected in tests cases because some of them were actually wrong: Within expansions the line information was being considered as originally computed in the macro definition, while the desired and expected for Creator's reporting mechanism (just like regular compilers) is the line from the expanded version of the tokens. - Do not allow for eager expansion. This was previously being done inside define directives. However, it's not allowed and might lead to incorrect results, since the argument substitution should only happen upon the macro invocation (and following nested ones). At least GCC and clang are consistent with that. See test case tst_Preprocessor:dont_eagerly_expand for a detailed explanation. - Revive the 'expanded' token flag. This is used to mark every token that originates from a macro expansion. Notice, however, that expanded tokens are not necessarily generated tokens (although every generated token is a expanded token). Expanded tokens that are not generated are those which are still considered by our code model features, since they are visible on the editor. The translation unit is smart enough to calculate line/column position for such tokens based on the information from the expansion section header. - How expansions are tracked has also changed. Now, we simply add two surrounding marker tokens to each "top-level" expansion sequence. There is an enumeration that control expansion states. Also, no "previous" token is kept around. - Preprocessor client methods suffered a change in signature so they now receive the line number of the action in question as a paramater. Previously such line could be retrieved by the client implementation by accessing the environment line. However, this is not reliable because we try to avoid synchronization of the output/environment lines in order to avoid unnecessary output, while expanding macros or handling preprocessor directives. - Although macros are not expanded during define directives (as mentioned above) the preprocessor client is now "notified" when it sees a macro. This is to allow usage tracking. - Other small stuff. This is all in one patch because the fixes are a consequence of the change in preprocessing control. Change-Id: I8f4c6e6366f37756ec65d0a93b79f72a3ac4ed50 Reviewed-by: Roberto Raggi <roberto.raggi@nokia.com>
2012-06-20 15:22:02 +02:00
sourceNeeded(0, absoluteFilePath, IncludeGlobal);
}
2008-12-08 11:08:48 +01:00
void CppPreprocessor::resetEnvironment()
{
env.reset();
m_processed.clear();
}
bool CppPreprocessor::includeFile(const QString &absoluteFilePath, QString *result, unsigned *revision)
2008-12-08 11:08:48 +01:00
{
if (absoluteFilePath.isEmpty() || m_included.contains(absoluteFilePath))
2008-12-08 11:08:48 +01:00
return true;
if (m_workingCopy.contains(absoluteFilePath)) {
m_included.insert(absoluteFilePath);
const QPair<QString, unsigned> r = m_workingCopy.get(absoluteFilePath);
*result = r.first;
*revision = r.second;
2008-12-08 11:08:48 +01:00
return true;
}
QFileInfo fileInfo(absoluteFilePath);
if (! fileInfo.isFile())
2008-12-02 12:01:29 +01:00
return false;
2008-12-08 11:08:48 +01:00
QFile file(absoluteFilePath);
if (file.open(QFile::ReadOnly | QFile::Text)) {
2008-12-08 11:08:48 +01:00
m_included.insert(absoluteFilePath);
QTextCodec *defaultCodec = Core::EditorManager::instance()->defaultTextCodec();
2008-12-08 11:08:48 +01:00
QTextStream stream(&file);
stream.setCodec(defaultCodec);
if (result)
*result = stream.readAll();
2008-12-08 11:08:48 +01:00
file.close();
return true;
2008-12-02 12:01:29 +01:00
}
2008-12-08 11:08:48 +01:00
return false;
}
QString CppPreprocessor::tryIncludeFile(QString &fileName, IncludeType type, unsigned *revision)
2010-05-10 09:54:30 +02:00
{
if (type == IncludeGlobal) {
const QString fn = m_fileNameCache.value(fileName);
if (! fn.isEmpty()) {
fileName = fn;
if (revision)
*revision = 0;
return QString();
}
}
const QString originalFileName = fileName;
const QString contents = tryIncludeFile_helper(fileName, type, revision);
if (type == IncludeGlobal)
m_fileNameCache.insert(originalFileName, fileName);
return contents;
}
static inline void appendDirSeparatorIfNeeded(QString &path)
{
if (!path.endsWith(QLatin1Char('/'), Qt::CaseInsensitive))
path += QLatin1Char('/');
}
2010-05-10 09:54:30 +02:00
QString CppPreprocessor::tryIncludeFile_helper(QString &fileName, IncludeType type, unsigned *revision)
2008-12-08 11:08:48 +01:00
{
QFileInfo fileInfo(fileName);
if (fileName == QLatin1String(pp_configuration_file) || fileInfo.isAbsolute()) {
QString contents;
includeFile(fileName, &contents, revision);
2008-12-08 11:08:48 +01:00
return contents;
}
if (type == IncludeLocal && m_currentDoc) {
QFileInfo currentFileInfo(m_currentDoc->fileName());
QString path = currentFileInfo.absolutePath();
appendDirSeparatorIfNeeded(path);
2008-12-08 11:08:48 +01:00
path += fileName;
path = QDir::cleanPath(path);
QString contents;
if (includeFile(path, &contents, revision)) {
2008-12-08 11:08:48 +01:00
fileName = path;
2008-12-02 12:01:29 +01:00
return contents;
}
2008-12-08 11:08:48 +01:00
}
2008-12-02 12:01:29 +01:00
foreach (const QString &includePath, m_includePaths) {
QString path = includePath;
appendDirSeparatorIfNeeded(path);
2008-12-08 11:08:48 +01:00
path += fileName;
path = QDir::cleanPath(path);
QString contents;
if (includeFile(path, &contents, revision)) {
2008-12-08 11:08:48 +01:00
fileName = path;
return contents;
2008-12-02 12:01:29 +01:00
}
2008-12-08 11:08:48 +01:00
}
2008-12-02 12:01:29 +01:00
2008-12-08 11:08:48 +01:00
// look in the system include paths
foreach (const QString &includePath, m_systemIncludePaths) {
QString path = includePath;
appendDirSeparatorIfNeeded(path);
2008-12-08 11:08:48 +01:00
path += fileName;
path = QDir::cleanPath(path);
QString contents;
if (includeFile(path, &contents, revision)) {
2008-12-08 11:08:48 +01:00
fileName = path;
return contents;
2008-12-02 12:01:29 +01:00
}
2008-12-08 11:08:48 +01:00
}
int index = fileName.indexOf(QLatin1Char('/'));
if (index != -1) {
QString frameworkName = fileName.left(index);
QString name = fileName.mid(index + 1);
2008-12-02 12:01:29 +01:00
2008-12-08 11:08:48 +01:00
foreach (const QString &frameworkPath, m_frameworkPaths) {
QString path = frameworkPath;
appendDirSeparatorIfNeeded(path);
2008-12-08 11:08:48 +01:00
path += frameworkName;
path += QLatin1String(".framework/Headers/");
path += name;
2009-06-02 14:56:03 +02:00
path = QDir::cleanPath(path);
QString contents;
if (includeFile(path, &contents, revision)) {
2008-12-02 12:01:29 +01:00
fileName = path;
return contents;
}
}
2008-12-08 11:08:48 +01:00
}
2008-12-02 12:01:29 +01:00
2008-12-08 11:08:48 +01:00
QString path = fileName;
if (path.at(0) != QLatin1Char('/'))
path.prepend(QLatin1Char('/'));
2008-12-02 12:01:29 +01:00
2008-12-08 11:08:48 +01:00
foreach (const QString &projectFile, m_projectFiles) {
if (projectFile.endsWith(path)) {
fileName = projectFile;
QString contents;
includeFile(fileName, &contents, revision);
2008-12-08 11:08:48 +01:00
return contents;
2008-12-02 12:01:29 +01:00
}
}
2008-12-08 11:08:48 +01:00
//qDebug() << "**** file" << fileName << "not found!";
return QString();
2008-12-08 11:08:48 +01:00
}
2008-12-02 12:01:29 +01:00
2008-12-08 12:59:33 +01:00
void CppPreprocessor::macroAdded(const Macro &macro)
2008-12-08 11:08:48 +01:00
{
if (! m_currentDoc)
return;
2008-12-02 12:01:29 +01:00
2008-12-08 12:59:33 +01:00
m_currentDoc->appendMacro(macro);
2008-12-08 11:08:48 +01:00
}
static inline const Macro revision(const CppModelManagerInterface::WorkingCopy &s, const Macro &macro)
{
Macro newMacro(macro);
newMacro.setFileRevision(s.get(macro.fileName()).second);
return newMacro;
}
C++: Core changes in preprocessing Summary of most relevant items: - Preprocessor output format change. No more gen true/false. Instead a more intuitive and natural expansion (like from a real compiler) is performed directly corresponding to the macro invocation. Notice that information about the generated tokens is not lost, because it's now embedded in the expansion section header (in terms of lines and columns as explained in the code). In addition the location on where the macro expansion happens is also documented for future use. - Fix line control directives and associated token line numbers. This was not detected in tests cases because some of them were actually wrong: Within expansions the line information was being considered as originally computed in the macro definition, while the desired and expected for Creator's reporting mechanism (just like regular compilers) is the line from the expanded version of the tokens. - Do not allow for eager expansion. This was previously being done inside define directives. However, it's not allowed and might lead to incorrect results, since the argument substitution should only happen upon the macro invocation (and following nested ones). At least GCC and clang are consistent with that. See test case tst_Preprocessor:dont_eagerly_expand for a detailed explanation. - Revive the 'expanded' token flag. This is used to mark every token that originates from a macro expansion. Notice, however, that expanded tokens are not necessarily generated tokens (although every generated token is a expanded token). Expanded tokens that are not generated are those which are still considered by our code model features, since they are visible on the editor. The translation unit is smart enough to calculate line/column position for such tokens based on the information from the expansion section header. - How expansions are tracked has also changed. Now, we simply add two surrounding marker tokens to each "top-level" expansion sequence. There is an enumeration that control expansion states. Also, no "previous" token is kept around. - Preprocessor client methods suffered a change in signature so they now receive the line number of the action in question as a paramater. Previously such line could be retrieved by the client implementation by accessing the environment line. However, this is not reliable because we try to avoid synchronization of the output/environment lines in order to avoid unnecessary output, while expanding macros or handling preprocessor directives. - Although macros are not expanded during define directives (as mentioned above) the preprocessor client is now "notified" when it sees a macro. This is to allow usage tracking. - Other small stuff. This is all in one patch because the fixes are a consequence of the change in preprocessing control. Change-Id: I8f4c6e6366f37756ec65d0a93b79f72a3ac4ed50 Reviewed-by: Roberto Raggi <roberto.raggi@nokia.com>
2012-06-20 15:22:02 +02:00
void CppPreprocessor::passedMacroDefinitionCheck(unsigned offset, unsigned line, const Macro &macro)
{
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);
}
C++: Core changes in preprocessing Summary of most relevant items: - Preprocessor output format change. No more gen true/false. Instead a more intuitive and natural expansion (like from a real compiler) is performed directly corresponding to the macro invocation. Notice that information about the generated tokens is not lost, because it's now embedded in the expansion section header (in terms of lines and columns as explained in the code). In addition the location on where the macro expansion happens is also documented for future use. - Fix line control directives and associated token line numbers. This was not detected in tests cases because some of them were actually wrong: Within expansions the line information was being considered as originally computed in the macro definition, while the desired and expected for Creator's reporting mechanism (just like regular compilers) is the line from the expanded version of the tokens. - Do not allow for eager expansion. This was previously being done inside define directives. However, it's not allowed and might lead to incorrect results, since the argument substitution should only happen upon the macro invocation (and following nested ones). At least GCC and clang are consistent with that. See test case tst_Preprocessor:dont_eagerly_expand for a detailed explanation. - Revive the 'expanded' token flag. This is used to mark every token that originates from a macro expansion. Notice, however, that expanded tokens are not necessarily generated tokens (although every generated token is a expanded token). Expanded tokens that are not generated are those which are still considered by our code model features, since they are visible on the editor. The translation unit is smart enough to calculate line/column position for such tokens based on the information from the expansion section header. - How expansions are tracked has also changed. Now, we simply add two surrounding marker tokens to each "top-level" expansion sequence. There is an enumeration that control expansion states. Also, no "previous" token is kept around. - Preprocessor client methods suffered a change in signature so they now receive the line number of the action in question as a paramater. Previously such line could be retrieved by the client implementation by accessing the environment line. However, this is not reliable because we try to avoid synchronization of the output/environment lines in order to avoid unnecessary output, while expanding macros or handling preprocessor directives. - Although macros are not expanded during define directives (as mentioned above) the preprocessor client is now "notified" when it sees a macro. This is to allow usage tracking. - Other small stuff. This is all in one patch because the fixes are a consequence of the change in preprocessing control. Change-Id: I8f4c6e6366f37756ec65d0a93b79f72a3ac4ed50 Reviewed-by: Roberto Raggi <roberto.raggi@nokia.com>
2012-06-20 15:22:02 +02:00
void CppPreprocessor::notifyMacroReference(unsigned offset, unsigned line, const Macro &macro)
{
if (! m_currentDoc)
return;
m_currentDoc->addMacroUse(revision(m_workingCopy, macro), offset, macro.name().length(), line,
C++: Core changes in preprocessing Summary of most relevant items: - Preprocessor output format change. No more gen true/false. Instead a more intuitive and natural expansion (like from a real compiler) is performed directly corresponding to the macro invocation. Notice that information about the generated tokens is not lost, because it's now embedded in the expansion section header (in terms of lines and columns as explained in the code). In addition the location on where the macro expansion happens is also documented for future use. - Fix line control directives and associated token line numbers. This was not detected in tests cases because some of them were actually wrong: Within expansions the line information was being considered as originally computed in the macro definition, while the desired and expected for Creator's reporting mechanism (just like regular compilers) is the line from the expanded version of the tokens. - Do not allow for eager expansion. This was previously being done inside define directives. However, it's not allowed and might lead to incorrect results, since the argument substitution should only happen upon the macro invocation (and following nested ones). At least GCC and clang are consistent with that. See test case tst_Preprocessor:dont_eagerly_expand for a detailed explanation. - Revive the 'expanded' token flag. This is used to mark every token that originates from a macro expansion. Notice, however, that expanded tokens are not necessarily generated tokens (although every generated token is a expanded token). Expanded tokens that are not generated are those which are still considered by our code model features, since they are visible on the editor. The translation unit is smart enough to calculate line/column position for such tokens based on the information from the expansion section header. - How expansions are tracked has also changed. Now, we simply add two surrounding marker tokens to each "top-level" expansion sequence. There is an enumeration that control expansion states. Also, no "previous" token is kept around. - Preprocessor client methods suffered a change in signature so they now receive the line number of the action in question as a paramater. Previously such line could be retrieved by the client implementation by accessing the environment line. However, this is not reliable because we try to avoid synchronization of the output/environment lines in order to avoid unnecessary output, while expanding macros or handling preprocessor directives. - Although macros are not expanded during define directives (as mentioned above) the preprocessor client is now "notified" when it sees a macro. This is to allow usage tracking. - Other small stuff. This is all in one patch because the fixes are a consequence of the change in preprocessing control. Change-Id: I8f4c6e6366f37756ec65d0a93b79f72a3ac4ed50 Reviewed-by: Roberto Raggi <roberto.raggi@nokia.com>
2012-06-20 15:22:02 +02:00
QVector<MacroArgumentReference>());
}
void CppPreprocessor::startExpandingMacro(unsigned offset, unsigned line,
2008-12-09 15:23:47 +01:00
const Macro &macro,
const QVector<MacroArgumentReference> &actuals)
2008-12-08 11:08:48 +01:00
{
if (! m_currentDoc)
return;
m_currentDoc->addMacroUse(revision(m_workingCopy, macro), offset, macro.name().length(), line, actuals);
2008-12-08 11:08:48 +01:00
}
void CppPreprocessor::stopExpandingMacro(unsigned, const Macro &)
2008-12-08 11:08:48 +01:00
{
if (! m_currentDoc)
return;
2008-12-02 12:01:29 +01:00
2008-12-08 11:08:48 +01:00
//qDebug() << "stop expanding:" << macro.name;
}
2008-12-02 12:01:29 +01:00
2008-12-08 11:08:48 +01:00
void CppPreprocessor::mergeEnvironment(Document::Ptr doc)
{
if (! doc)
return;
2008-12-02 12:01:29 +01:00
2008-12-08 11:08:48 +01:00
const QString fn = doc->fileName();
2008-12-02 12:01:29 +01:00
if (m_processed.contains(fn))
2008-12-08 11:08:48 +01:00
return;
2008-12-02 12:01:29 +01:00
m_processed.insert(fn);
2008-12-02 12:01:29 +01:00
foreach (const Document::Include &incl, doc->includes()) {
QString includedFile = incl.fileName();
if (Document::Ptr includedDoc = snapshot.document(includedFile))
mergeEnvironment(includedDoc);
else
run(includedFile);
2008-12-08 12:59:33 +01:00
}
2008-12-02 12:01:29 +01:00
env.addMacros(doc->definedMacros());
2008-12-08 11:08:48 +01:00
}
2008-12-02 12:01:29 +01:00
2008-12-08 11:08:48 +01:00
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);
}
C++: Core changes in preprocessing Summary of most relevant items: - Preprocessor output format change. No more gen true/false. Instead a more intuitive and natural expansion (like from a real compiler) is performed directly corresponding to the macro invocation. Notice that information about the generated tokens is not lost, because it's now embedded in the expansion section header (in terms of lines and columns as explained in the code). In addition the location on where the macro expansion happens is also documented for future use. - Fix line control directives and associated token line numbers. This was not detected in tests cases because some of them were actually wrong: Within expansions the line information was being considered as originally computed in the macro definition, while the desired and expected for Creator's reporting mechanism (just like regular compilers) is the line from the expanded version of the tokens. - Do not allow for eager expansion. This was previously being done inside define directives. However, it's not allowed and might lead to incorrect results, since the argument substitution should only happen upon the macro invocation (and following nested ones). At least GCC and clang are consistent with that. See test case tst_Preprocessor:dont_eagerly_expand for a detailed explanation. - Revive the 'expanded' token flag. This is used to mark every token that originates from a macro expansion. Notice, however, that expanded tokens are not necessarily generated tokens (although every generated token is a expanded token). Expanded tokens that are not generated are those which are still considered by our code model features, since they are visible on the editor. The translation unit is smart enough to calculate line/column position for such tokens based on the information from the expansion section header. - How expansions are tracked has also changed. Now, we simply add two surrounding marker tokens to each "top-level" expansion sequence. There is an enumeration that control expansion states. Also, no "previous" token is kept around. - Preprocessor client methods suffered a change in signature so they now receive the line number of the action in question as a paramater. Previously such line could be retrieved by the client implementation by accessing the environment line. However, this is not reliable because we try to avoid synchronization of the output/environment lines in order to avoid unnecessary output, while expanding macros or handling preprocessor directives. - Although macros are not expanded during define directives (as mentioned above) the preprocessor client is now "notified" when it sees a macro. This is to allow usage tracking. - Other small stuff. This is all in one patch because the fixes are a consequence of the change in preprocessing control. Change-Id: I8f4c6e6366f37756ec65d0a93b79f72a3ac4ed50 Reviewed-by: Roberto Raggi <roberto.raggi@nokia.com>
2012-06-20 15:22:02 +02:00
void CppPreprocessor::sourceNeeded(unsigned line, QString &fileName, IncludeType type)
2008-12-08 11:08:48 +01:00
{
if (fileName.isEmpty())
return;
unsigned editorRevision = 0;
QString contents = tryIncludeFile(fileName, type, &editorRevision);
fileName = QDir::cleanPath(fileName);
2008-12-08 11:08:48 +01:00
if (m_currentDoc) {
m_currentDoc->addIncludeFile(fileName, line);
2008-12-08 11:08:48 +01:00
if (contents.isEmpty() && ! QFileInfo(fileName).isAbsolute()) {
QString msg = QCoreApplication::translate(
"CppPreprocessor", "%1: No such file or directory").arg(fileName);
2008-12-08 11:08:48 +01:00
Document::DiagnosticMessage d(Document::DiagnosticMessage::Warning,
m_currentDoc->fileName(),
C++: Core changes in preprocessing Summary of most relevant items: - Preprocessor output format change. No more gen true/false. Instead a more intuitive and natural expansion (like from a real compiler) is performed directly corresponding to the macro invocation. Notice that information about the generated tokens is not lost, because it's now embedded in the expansion section header (in terms of lines and columns as explained in the code). In addition the location on where the macro expansion happens is also documented for future use. - Fix line control directives and associated token line numbers. This was not detected in tests cases because some of them were actually wrong: Within expansions the line information was being considered as originally computed in the macro definition, while the desired and expected for Creator's reporting mechanism (just like regular compilers) is the line from the expanded version of the tokens. - Do not allow for eager expansion. This was previously being done inside define directives. However, it's not allowed and might lead to incorrect results, since the argument substitution should only happen upon the macro invocation (and following nested ones). At least GCC and clang are consistent with that. See test case tst_Preprocessor:dont_eagerly_expand for a detailed explanation. - Revive the 'expanded' token flag. This is used to mark every token that originates from a macro expansion. Notice, however, that expanded tokens are not necessarily generated tokens (although every generated token is a expanded token). Expanded tokens that are not generated are those which are still considered by our code model features, since they are visible on the editor. The translation unit is smart enough to calculate line/column position for such tokens based on the information from the expansion section header. - How expansions are tracked has also changed. Now, we simply add two surrounding marker tokens to each "top-level" expansion sequence. There is an enumeration that control expansion states. Also, no "previous" token is kept around. - Preprocessor client methods suffered a change in signature so they now receive the line number of the action in question as a paramater. Previously such line could be retrieved by the client implementation by accessing the environment line. However, this is not reliable because we try to avoid synchronization of the output/environment lines in order to avoid unnecessary output, while expanding macros or handling preprocessor directives. - Although macros are not expanded during define directives (as mentioned above) the preprocessor client is now "notified" when it sees a macro. This is to allow usage tracking. - Other small stuff. This is all in one patch because the fixes are a consequence of the change in preprocessing control. Change-Id: I8f4c6e6366f37756ec65d0a93b79f72a3ac4ed50 Reviewed-by: Roberto Raggi <roberto.raggi@nokia.com>
2012-06-20 15:22:02 +02:00
line, /*column = */ 0,
2008-12-08 11:08:48 +01:00
msg);
2008-12-08 11:08:48 +01:00
m_currentDoc->addDiagnosticMessage(d);
2008-12-08 11:08:48 +01:00
//qWarning() << "file not found:" << fileName << m_currentDoc->fileName() << env.current_line;
2008-12-02 12:01:29 +01:00
}
2008-12-08 11:08:48 +01:00
}
2008-12-02 12:01:29 +01:00
if (m_dumpFileNameWhileParsing) {
qDebug() << "Parsing file:" << fileName
// << "contents:" << contents.size()
;
}
2008-12-02 12:01:29 +01:00
Document::Ptr doc = snapshot.document(fileName);
if (doc) {
mergeEnvironment(doc);
return;
}
2008-12-02 12:01:29 +01:00
doc = Document::create(fileName);
doc->setRevision(m_revision);
doc->setEditorRevision(editorRevision);
2008-12-02 12:01:29 +01:00
QFileInfo info(fileName);
if (info.exists())
doc->setLastModified(info.lastModified());
Document::Ptr previousDoc = switchDocument(doc);
2008-12-02 12:01:29 +01:00
const QByteArray preprocessedCode = preprocess.run(fileName, contents);
2008-12-02 12:01:29 +01:00
// { 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();
snapshot.insert(doc);
m_todo.remove(fileName);
2008-12-02 12:01:29 +01:00
2010-05-10 10:19:37 +02:00
Process process(m_modelManager, doc, snapshot, m_workingCopy);
2010-05-10 10:19:37 +02:00
process();
(void) switchDocument(previousDoc);
2008-12-08 11:08:48 +01:00
}
2008-12-02 12:01:29 +01:00
2008-12-08 11:08:48 +01:00
Document::Ptr CppPreprocessor::switchDocument(Document::Ptr doc)
{
Document::Ptr previousDoc = m_currentDoc;
m_currentDoc = doc;
return previousDoc;
}
2008-12-02 12:01:29 +01:00
void CppModelManager::updateModifiedSourceFiles()
{
const Snapshot snapshot = this->snapshot();
QStringList sourceFiles;
foreach (const Document::Ptr doc, snapshot) {
const QDateTime lastModified = doc->lastModified();
if (! lastModified.isNull()) {
QFileInfo fileInfo(doc->fileName());
if (fileInfo.exists() && fileInfo.lastModified() != lastModified)
sourceFiles.append(doc->fileName());
}
}
updateSourceFiles(sourceFiles);
}
2008-12-02 12:01:29 +01:00
/*!
\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
2009-05-08 13:01:32 +02:00
modified within Qt Creator.
2008-12-02 12:01:29 +01:00
*/
QMutex CppModelManager::m_modelManagerMutex;
CppModelManager *CppModelManager::m_modelManagerInstance = 0;
CppModelManager *CppModelManager::instance()
{
if (m_modelManagerInstance)
return m_modelManagerInstance;
QMutexLocker locker(&m_modelManagerMutex);
if (!m_modelManagerInstance) {
m_modelManagerInstance = new CppModelManager;
}
return m_modelManagerInstance;
}
CppModelManager::CppModelManager(QObject *parent)
: CppModelManagerInterface(parent)
2008-12-02 12:01:29 +01:00
{
2009-08-07 13:02:36 +02:00
m_findReferences = new CppFindReferences(this);
m_indexerEnabled = qgetenv("QTCREATOR_NO_CODE_INDEXER").isNull();
m_dumpFileNameWhileParsing = !qgetenv("QTCREATOR_DUMP_FILENAME_WHILE_PARSING").isNull();
2009-08-07 13:02:36 +02:00
m_revision = 0;
2009-03-11 12:00:07 +01:00
m_synchronizer.setCancelOnWait(true);
2008-12-04 17:07:43 +01:00
m_dirty = true;
ProjectExplorer::ProjectExplorerPlugin *pe =
ProjectExplorer::ProjectExplorerPlugin::instance();
2008-12-02 12:01:29 +01:00
QTC_ASSERT(pe, return);
2008-12-02 12:01:29 +01:00
ProjectExplorer::SessionManager *session = pe->session();
2009-02-10 22:56:04 +01:00
m_updateEditorSelectionsTimer = new QTimer(this);
m_updateEditorSelectionsTimer->setInterval(500);
m_updateEditorSelectionsTimer->setSingleShot(true);
connect(m_updateEditorSelectionsTimer, SIGNAL(timeout()),
this, SLOT(updateEditorSelections()));
2008-12-04 17:07:43 +01:00
connect(session, SIGNAL(projectAdded(ProjectExplorer::Project*)),
this, SLOT(onProjectAdded(ProjectExplorer::Project*)));
connect(session, SIGNAL(aboutToRemoveProject(ProjectExplorer::Project*)),
this, SLOT(onAboutToRemoveProject(ProjectExplorer::Project*)));
2008-12-02 12:01:29 +01:00
connect(session, SIGNAL(aboutToUnloadSession(QString)),
this, SLOT(onAboutToUnloadSession()));
2008-12-02 12:01:29 +01:00
qRegisterMetaType<CPlusPlus::Document::Ptr>("CPlusPlus::Document::Ptr");
// thread connections
connect(this, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)),
this, SLOT(onDocumentUpdated(CPlusPlus::Document::Ptr)));
connect(this, SIGNAL(extraDiagnosticsUpdated(QString)),
this, SLOT(onExtraDiagnosticsUpdated(QString)));
2008-12-02 12:01:29 +01:00
// Listen for editor closed and opened events so that we can keep track of changing files
connect(Core::ICore::editorManager(), SIGNAL(editorOpened(Core::IEditor*)),
this, SLOT(editorOpened(Core::IEditor*)));
2008-12-02 12:01:29 +01:00
connect(Core::ICore::editorManager(), SIGNAL(editorAboutToClose(Core::IEditor*)),
this, SLOT(editorAboutToClose(Core::IEditor*)));
m_completionFallback = new InternalCompletionAssistProvider;
m_completionAssistProvider = m_completionFallback;
ExtensionSystem::PluginManager::addObject(m_completionAssistProvider);
m_highlightingFallback = new CppHighlightingSupportInternalFactory;
m_highlightingFactory = m_highlightingFallback;
2008-12-02 12:01:29 +01:00
}
CppModelManager::~CppModelManager()
{
ExtensionSystem::PluginManager::removeObject(m_completionAssistProvider);
delete m_completionFallback;
delete m_highlightingFallback;
}
2008-12-02 12:01:29 +01:00
Snapshot CppModelManager::snapshot() const
{
2009-06-02 15:27:13 +02:00
QMutexLocker locker(&protectSnapshot);
return m_snapshot;
}
2008-12-02 12:01:29 +01:00
void CppModelManager::ensureUpdated()
{
QMutexLocker locker(&mutex);
if (! m_dirty)
return;
2008-12-08 14:48:51 +01:00
m_projectFiles = internalProjectFiles();
m_includePaths = internalIncludePaths();
m_frameworkPaths = internalFrameworkPaths();
m_definedMacros = internalDefinedMacros();
m_dirty = false;
}
2008-12-08 14:48:51 +01:00
QStringList CppModelManager::internalProjectFiles() const
2008-12-02 12:01:29 +01:00
{
QStringList files;
QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects);
while (it.hasNext()) {
it.next();
ProjectInfo pinfo = it.value();
foreach (const ProjectPart::Ptr &part, pinfo.projectParts())
files += part->sourceFiles;
2008-12-02 12:01:29 +01:00
}
2008-12-08 14:48:51 +01:00
files.removeDuplicates();
2008-12-02 12:01:29 +01:00
return files;
}
2008-12-08 14:48:51 +01:00
QStringList CppModelManager::internalIncludePaths() const
2008-12-02 12:01:29 +01:00
{
QStringList includePaths;
QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects);
while (it.hasNext()) {
it.next();
ProjectInfo pinfo = it.value();
foreach (const ProjectPart::Ptr &part, pinfo.projectParts())
includePaths += part->includePaths;
2008-12-02 12:01:29 +01:00
}
2008-12-08 14:48:51 +01:00
includePaths.removeDuplicates();
2008-12-02 12:01:29 +01:00
return includePaths;
}
2008-12-08 14:48:51 +01:00
QStringList CppModelManager::internalFrameworkPaths() const
2008-12-02 12:01:29 +01:00
{
QStringList frameworkPaths;
QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects);
while (it.hasNext()) {
it.next();
ProjectInfo pinfo = it.value();
foreach (const ProjectPart::Ptr &part, pinfo.projectParts())
frameworkPaths += part->frameworkPaths;
2008-12-02 12:01:29 +01:00
}
2008-12-08 14:48:51 +01:00
frameworkPaths.removeDuplicates();
2008-12-02 12:01:29 +01:00
return frameworkPaths;
}
2008-12-08 14:48:51 +01:00
QByteArray CppModelManager::internalDefinedMacros() const
2008-12-02 12:01:29 +01:00
{
QByteArray macros;
QSet<QByteArray> alreadyIn;
2008-12-02 12:01:29 +01:00
QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects);
while (it.hasNext()) {
it.next();
ProjectInfo pinfo = it.value();
foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
const QList<QByteArray> defs = part->defines.split('\n');
foreach (const QByteArray &def, defs) {
if (!alreadyIn.contains(def)) {
macros += def;
macros.append('\n');
alreadyIn.insert(def);
}
}
}
2008-12-02 12:01:29 +01:00
}
return macros;
}
void CppModelManager::addEditorSupport(AbstractEditorSupport *editorSupport)
{
m_addtionalEditorSupport.insert(editorSupport);
}
void CppModelManager::removeEditorSupport(AbstractEditorSupport *editorSupport)
{
m_addtionalEditorSupport.remove(editorSupport);
}
QList<int> CppModelManager::references(CPlusPlus::Symbol *symbol, const LookupContext &context)
{
return m_findReferences->references(symbol, context);
}
void CppModelManager::findUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context)
2009-08-07 13:02:36 +02:00
{
if (symbol->identifier())
m_findReferences->findUsages(symbol, context);
2009-10-05 15:17:25 +02:00
}
void CppModelManager::renameUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context,
const QString &replacement)
2009-10-05 15:17:25 +02:00
{
if (symbol->identifier())
m_findReferences->renameUsages(symbol, context, replacement);
2009-08-07 13:02:36 +02:00
}
void CppModelManager::findMacroUsages(const CPlusPlus::Macro &macro)
{
m_findReferences->findMacroUses(macro);
}
void CppModelManager::renameMacroUsages(const CPlusPlus::Macro &macro, const QString &replacement)
{
m_findReferences->renameMacroUses(macro, replacement);
}
CppModelManager::WorkingCopy CppModelManager::buildWorkingCopyList()
2008-12-02 12:01:29 +01:00
{
WorkingCopy workingCopy;
2008-12-02 12:01:29 +01:00
QMapIterator<TextEditor::ITextEditor *, CppEditorSupport *> it(m_editorSupport);
while (it.hasNext()) {
it.next();
TextEditor::ITextEditor *textEditor = it.key();
CppEditorSupport *editorSupport = it.value();
QString fileName = textEditor->document()->fileName();
workingCopy.insert(fileName, editorSupport->contents(), editorSupport->editorRevision());
2008-12-02 12:01:29 +01:00
}
QSetIterator<AbstractEditorSupport *> jt(m_addtionalEditorSupport);
while (jt.hasNext()) {
AbstractEditorSupport *es = jt.next();
workingCopy.insert(es->fileName(), es->contents());
}
2008-12-02 12:01:29 +01:00
// add the project configuration file
QByteArray conf(pp_configuration);
conf += definedMacros();
workingCopy.insert(pp_configuration_file, conf);
2008-12-02 12:01:29 +01:00
return workingCopy;
}
CppModelManager::WorkingCopy CppModelManager::workingCopy() const
{
return const_cast<CppModelManager *>(this)->buildWorkingCopyList();
}
2010-03-02 12:51:47 +01:00
QFuture<void> CppModelManager::updateSourceFiles(const QStringList &sourceFiles)
{ return refreshSourceFiles(sourceFiles); }
2008-12-02 12:01:29 +01:00
QList<CppModelManager::ProjectInfo> CppModelManager::projectInfos() const
{
QMutexLocker locker(&mutex);
return m_projects.values();
}
CppModelManager::ProjectInfo CppModelManager::projectInfo(ProjectExplorer::Project *project) const
{
QMutexLocker locker(&mutex);
return m_projects.value(project, ProjectInfo(project));
}
void CppModelManager::updateProjectInfo(const ProjectInfo &pinfo)
{
#if 0
// Tons of debug output...
qDebug()<<"========= CppModelManager::updateProjectInfo ======";
qDebug()<<" for project:"<< pinfo.project().data()->document()->fileName();
foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
qDebug() << "=== part ===";
qDebug() << "language:" << (part->language == CXX ? "C++" : "ObjC++");
qDebug() << "C++11:" << part->cxx11Enabled;
qDebug() << "Qt version:" << part->qtVersion;
qDebug() << "precompiled header:" << part->precompiledHeaders;
qDebug() << "defines:" << part->defines;
qDebug() << "includes:" << part->includePaths;
qDebug() << "frameworkPaths:" << part->frameworkPaths;
qDebug() << "sources:" << part->sourceFiles;
qDebug() << "";
}
qDebug() << "";
#endif
QMutexLocker locker(&mutex);
if (! pinfo.isValid())
return;
ProjectExplorer::Project *project = pinfo.project().data();
m_projects.insert(project, pinfo);
2008-12-08 14:48:51 +01:00
m_dirty = true;
m_srcToProjectPart.clear();
foreach (const ProjectInfo &projectInfo, m_projects.values())
foreach (const ProjectPart::Ptr &projectPart, projectInfo.projectParts())
foreach (const QString &sourceFile, projectPart->sourceFiles)
m_srcToProjectPart[sourceFile].append(projectPart);
}
QList<CppModelManager::ProjectPart::Ptr> CppModelManager::projectPart(const QString &fileName) const
{
QList<CppModelManager::ProjectPart::Ptr> parts = m_srcToProjectPart.value(fileName);
if (!parts.isEmpty())
return parts;
//### FIXME: This is a DIRTY hack!
if (fileName.endsWith(".h")) {
QString cppFile = fileName.mid(0, fileName.length() - 2) + QLatin1String(".cpp");
parts = m_srcToProjectPart.value(cppFile);
if (!parts.isEmpty())
return parts;
}
DependencyTable table;
table.build(snapshot());
QStringList deps = table.filesDependingOn(fileName);
foreach (const QString &dep, deps) {
parts = m_srcToProjectPart.value(dep);
if (!parts.isEmpty())
return parts;
}
return parts;
}
2008-12-02 12:01:29 +01:00
QFuture<void> CppModelManager::refreshSourceFiles(const QStringList &sourceFiles)
{
if (! sourceFiles.isEmpty() && m_indexerEnabled) {
const WorkingCopy workingCopy = buildWorkingCopyList();
2008-12-02 12:01:29 +01:00
CppPreprocessor *preproc = new CppPreprocessor(this, m_dumpFileNameWhileParsing);
preproc->setRevision(++m_revision);
2008-12-04 17:07:43 +01:00
preproc->setProjectFiles(projectFiles());
preproc->setIncludePaths(includePaths());
preproc->setFrameworkPaths(frameworkPaths());
preproc->setWorkingCopy(workingCopy);
QFuture<void> result = QtConcurrent::run(&CppModelManager::parse,
preproc, sourceFiles);
2008-12-02 12:01:29 +01:00
2009-03-11 12:21:02 +01:00
if (m_synchronizer.futures().size() > 10) {
QList<QFuture<void> > futures = m_synchronizer.futures();
m_synchronizer.clearFutures();
foreach (const QFuture<void> &future, futures) {
2009-03-11 12:21:02 +01:00
if (! (future.isFinished() || future.isCanceled()))
m_synchronizer.addFuture(future);
}
}
2009-03-11 12:00:07 +01:00
m_synchronizer.addFuture(result);
if (sourceFiles.count() > 1) {
Core::ICore::progressManager()->addTask(result, tr("Parsing"),
CppTools::Constants::TASK_INDEX);
2008-12-02 12:01:29 +01:00
}
2008-12-02 12:01:29 +01:00
return result;
}
return QFuture<void>();
}
/*!
\fn void CppModelManager::editorOpened(Core::IEditor *editor)
\brief If a C++ editor is opened, the model manager listens to content changes
in order to update the CppCodeModel accordingly. It also updates the
CppCodeModel for the first time with this editor.
\sa void CppModelManager::editorContentsChanged()
*/
void CppModelManager::editorOpened(Core::IEditor *editor)
{
if (isCppEditor(editor)) {
TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor *>(editor);
2008-12-09 15:25:01 +01:00
QTC_ASSERT(textEditor, return);
2008-12-02 12:01:29 +01:00
CppEditorSupport *editorSupport = new CppEditorSupport(this);
editorSupport->setTextEditor(textEditor);
m_editorSupport[textEditor] = editorSupport;
}
}
void CppModelManager::editorAboutToClose(Core::IEditor *editor)
{
if (isCppEditor(editor)) {
TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor *>(editor);
2008-12-09 15:25:01 +01:00
QTC_ASSERT(textEditor, return);
2008-12-02 12:01:29 +01:00
CppEditorSupport *editorSupport = m_editorSupport.value(textEditor);
m_editorSupport.remove(textEditor);
delete editorSupport;
}
}
bool CppModelManager::isCppEditor(Core::IEditor *editor) const
{
return editor->context().contains(ProjectExplorer::Constants::LANG_CXX);
2008-12-02 12:01:29 +01:00
}
void CppModelManager::emitDocumentUpdated(Document::Ptr doc)
{
emit documentUpdated(doc);
}
2008-12-02 12:01:29 +01:00
void CppModelManager::onDocumentUpdated(Document::Ptr doc)
{
const QString fileName = doc->fileName();
2009-06-02 15:27:13 +02:00
bool outdated = false;
2009-06-02 15:27:13 +02:00
protectSnapshot.lock();
Document::Ptr previous = m_snapshot.document(fileName);
if (previous && (doc->revision() != 0 && doc->revision() < previous->revision()))
outdated = true;
else
m_snapshot.insert(doc);
2009-06-02 15:27:13 +02:00
protectSnapshot.unlock();
if (outdated)
return;
updateEditor(doc);
}
void CppModelManager::onExtraDiagnosticsUpdated(const QString &fileName)
{
protectSnapshot.lock();
Document::Ptr doc = m_snapshot.document(fileName);
protectSnapshot.unlock();
if (doc)
updateEditor(doc);
}
void CppModelManager::updateEditor(Document::Ptr doc)
{
const QString fileName = doc->fileName();
QList<Core::IEditor *> openedEditors = Core::ICore::editorManager()->openedEditors();
2008-12-02 12:01:29 +01:00
foreach (Core::IEditor *editor, openedEditors) {
if (editor->document()->fileName() == fileName) {
2008-12-02 12:01:29 +01:00
TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor *>(editor);
if (! textEditor)
continue;
TextEditor::BaseTextEditorWidget *ed = qobject_cast<TextEditor::BaseTextEditorWidget *>(textEditor->widget());
2008-12-02 12:01:29 +01:00
if (! ed)
continue;
QList<TextEditor::BaseTextEditorWidget::BlockRange> blockRanges;
2008-12-02 12:01:29 +01:00
foreach (const Document::Block &block, doc->skippedBlocks()) {
blockRanges.append(TextEditor::BaseTextEditorWidget::BlockRange(block.begin(), block.end()));
2008-12-02 12:01:29 +01:00
}
// set up the format for the errors
QTextCharFormat errorFormat;
errorFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline);
errorFormat.setUnderlineColor(Qt::red);
// set up the format for the warnings.
QTextCharFormat warningFormat;
warningFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline);
warningFormat.setUnderlineColor(Qt::darkYellow);
2009-02-10 22:56:04 +01:00
QList<Editor> todo;
foreach (const Editor &e, m_todo) {
if (e.textEditor != textEditor)
2009-02-10 22:56:04 +01:00
todo.append(e);
}
Editor e;
if (m_highlightingFactory->hightlighterHandlesDiagnostics()) {
e.updateSelections = false;
} else {
QSet<int> lines;
QList<Document::DiagnosticMessage> messages = doc->diagnosticMessages();
messages += extraDiagnostics(doc->fileName());
foreach (const Document::DiagnosticMessage &m, messages) {
if (m.fileName() != fileName)
continue;
else if (lines.contains(m.line()))
continue;
lines.insert(m.line());
QTextEdit::ExtraSelection sel;
if (m.isWarning())
sel.format = warningFormat;
else
sel.format = errorFormat;
QTextCursor c(ed->document()->findBlockByNumber(m.line() - 1));
const QString text = c.block().text();
if (m.length() > 0 && m.column() + m.length() < (unsigned)text.size()) {
int column = m.column() > 0 ? m.column() - 1 : 0;
c.setPosition(c.position() + column);
c.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, m.length());
} else {
for (int i = 0; i < text.size(); ++i) {
if (! text.at(i).isSpace()) {
c.setPosition(c.position() + i);
break;
}
}
c.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
}
sel.cursor = c;
sel.format.setToolTip(m.text());
e.selections.append(sel);
}
}
e.revision = ed->document()->revision();
e.textEditor = textEditor;
e.ifdefedOutBlocks = blockRanges;
2009-02-10 22:56:04 +01:00
todo.append(e);
m_todo = todo;
postEditorUpdate();
2008-12-02 12:01:29 +01:00
break;
}
}
}
2009-02-10 22:56:04 +01:00
void CppModelManager::postEditorUpdate()
{
m_updateEditorSelectionsTimer->start(500);
}
void CppModelManager::updateEditorSelections()
{
foreach (const Editor &ed, m_todo) {
if (! ed.textEditor)
2009-02-10 22:56:04 +01:00
continue;
TextEditor::ITextEditor *textEditor = ed.textEditor;
TextEditor::BaseTextEditorWidget *editor = qobject_cast<TextEditor::BaseTextEditorWidget *>(textEditor->widget());
if (! editor)
continue;
else if (editor->document()->revision() != ed.revision)
continue; // outdated
if (ed.updateSelections)
editor->setExtraSelections(TextEditor::BaseTextEditorWidget::CodeWarningsSelection,
ed.selections);
editor->setIfdefedOutBlocks(ed.ifdefedOutBlocks);
2009-02-10 22:56:04 +01:00
}
m_todo.clear();
2009-02-10 22:56:04 +01:00
}
2008-12-04 17:07:43 +01:00
void CppModelManager::onProjectAdded(ProjectExplorer::Project *)
{
QMutexLocker locker(&mutex);
2008-12-04 17:07:43 +01:00
m_dirty = true;
}
2008-12-02 12:01:29 +01:00
void CppModelManager::onAboutToRemoveProject(ProjectExplorer::Project *project)
{
do {
QMutexLocker locker(&mutex);
m_dirty = true;
m_projects.remove(project);
} while (0);
2008-12-02 12:01:29 +01:00
GC();
}
void CppModelManager::onAboutToUnloadSession()
2008-12-02 12:01:29 +01:00
{
if (Core::ProgressManager *pm = Core::ICore::progressManager()) {
pm->cancelTasks(CppTools::Constants::TASK_INDEX);
2008-12-04 17:07:43 +01:00
}
do {
QMutexLocker locker(&mutex);
m_projects.clear();
m_dirty = true;
} while (0);
GC();
2008-12-02 12:01:29 +01:00
}
void CppModelManager::parse(QFutureInterface<void> &future,
2008-12-04 17:07:43 +01:00
CppPreprocessor *preproc,
QStringList files)
2008-12-02 12:01:29 +01:00
{
if (files.isEmpty())
return;
const Core::MimeDatabase *mimeDb = Core::ICore::mimeDatabase();
Core::MimeType cSourceTy = mimeDb->findByType(QLatin1String("text/x-csrc"));
Core::MimeType cppSourceTy = mimeDb->findByType(QLatin1String("text/x-c++src"));
Core::MimeType mSourceTy = mimeDb->findByType(QLatin1String("text/x-objcsrc"));
QStringList sources;
QStringList headers;
QStringList suffixes = cSourceTy.suffixes();
suffixes += cppSourceTy.suffixes();
suffixes += mSourceTy.suffixes();
foreach (const QString &file, files) {
QFileInfo info(file);
preproc->snapshot.remove(file);
if (suffixes.contains(info.suffix()))
sources.append(file);
else
headers.append(file);
}
const int sourceCount = sources.size();
files = sources;
files += headers;
2009-02-23 15:57:37 +01:00
preproc->setTodo(files);
2008-12-02 12:01:29 +01:00
future.setProgressRange(0, files.size());
QString conf = QLatin1String(pp_configuration_file);
bool processingHeaders = false;
2008-12-02 12:01:29 +01:00
for (int i = 0; i < files.size(); ++i) {
if (future.isPaused())
future.waitForResume();
if (future.isCanceled())
break;
const QString fileName = files.at(i);
const bool isSourceFile = i < sourceCount;
if (isSourceFile)
(void) preproc->run(conf);
else if (! processingHeaders) {
(void) preproc->run(conf);
processingHeaders = true;
}
2008-12-04 17:07:43 +01:00
preproc->run(fileName);
2008-12-02 12:01:29 +01:00
2009-02-23 15:57:37 +01:00
future.setProgressValue(files.size() - preproc->todo().size());
if (isSourceFile)
preproc->resetEnvironment();
2008-12-02 12:01:29 +01:00
}
future.setProgressValue(files.size());
preproc->modelManager()->finishedRefreshingSourceFiles(files);
2008-12-04 17:07:43 +01:00
delete preproc;
2008-12-02 12:01:29 +01:00
}
void CppModelManager::GC()
{
2009-06-02 15:27:13 +02:00
protectSnapshot.lock();
Snapshot currentSnapshot = m_snapshot;
2009-06-02 15:27:13 +02:00
protectSnapshot.unlock();
2008-12-02 12:01:29 +01:00
QSet<QString> processed;
2008-12-04 17:07:43 +01:00
QStringList todo = projectFiles();
2008-12-02 12:01:29 +01:00
while (! todo.isEmpty()) {
QString fn = todo.last();
todo.removeLast();
if (processed.contains(fn))
continue;
processed.insert(fn);
if (Document::Ptr doc = currentSnapshot.document(fn)) {
2008-12-02 12:01:29 +01:00
todo += doc->includedFiles();
}
}
QStringList removedFiles;
Snapshot newSnapshot;
for (Snapshot::const_iterator it = currentSnapshot.begin(); it != currentSnapshot.end(); ++it) {
const QString fileName = it.key();
if (processed.contains(fileName))
newSnapshot.insert(it.value());
else
removedFiles.append(fileName);
2008-12-02 12:01:29 +01:00
}
emit aboutToRemoveFiles(removedFiles);
2009-06-02 15:27:13 +02:00
protectSnapshot.lock();
m_snapshot = newSnapshot;
2009-06-02 15:27:13 +02:00
protectSnapshot.unlock();
2008-12-02 12:01:29 +01:00
}
void CppModelManager::finishedRefreshingSourceFiles(const QStringList &files)
{
emit sourceFilesRefreshed(files);
}
CppCompletionSupport *CppModelManager::completionSupport(Core::IEditor *editor) const
{
if (TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor *>(editor))
return m_completionAssistProvider->completionSupport(textEditor);
else
return 0;
}
void CppModelManager::setCppCompletionAssistProvider(CppCompletionAssistProvider *completionAssistProvider)
{
ExtensionSystem::PluginManager::removeObject(m_completionAssistProvider);
if (completionAssistProvider)
m_completionAssistProvider = completionAssistProvider;
else
m_completionAssistProvider = m_completionFallback;
ExtensionSystem::PluginManager::addObject(m_completionAssistProvider);
}
CppHighlightingSupport *CppModelManager::highlightingSupport(Core::IEditor *editor) const
{
if (TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor *>(editor))
return m_highlightingFactory->highlightingSupport(textEditor);
else
return 0;
}
void CppModelManager::setHighlightingSupportFactory(CppHighlightingSupportFactory *highlightingFactory)
{
if (highlightingFactory)
m_highlightingFactory = highlightingFactory;
else
m_highlightingFactory = m_highlightingFallback;
}
void CppModelManager::setExtraDiagnostics(const QString &fileName, int kind,
const QList<Document::DiagnosticMessage> &diagnostics)
{
{
QMutexLocker locker(&protectExtraDiagnostics);
if (m_extraDiagnostics[fileName][kind] == diagnostics)
return;
m_extraDiagnostics[fileName].insert(kind, diagnostics);
}
emit extraDiagnosticsUpdated(fileName);
}
QList<Document::DiagnosticMessage> CppModelManager::extraDiagnostics(const QString &fileName, int kind) const
{
QMutexLocker locker(&protectExtraDiagnostics);
if (kind == -1) {
QList<Document::DiagnosticMessage> messages;
foreach (const QList<Document::DiagnosticMessage> &list, m_extraDiagnostics.value(fileName))
messages += list;
return messages;
}
return m_extraDiagnostics.value(fileName).value(kind);
}