Clang: Provide highlighting for identifier under cursor

Change-Id: I80ffe23cbcc84ab7323124581d9dd6afbe974fd0
Reviewed-by: Marco Bubke <marco.bubke@qt.io>
This commit is contained in:
Nikolai Kosjar
2017-06-09 12:19:09 +02:00
parent 9de9da7423
commit ca72c29462
59 changed files with 2050 additions and 11 deletions

View File

@@ -39,6 +39,7 @@ SOURCES += $$PWD/clangcodemodelserverinterface.cpp \
$$PWD/sourcelocationcontainer.cpp \
$$PWD/fixitcontainer.cpp \
$$PWD/requestdocumentannotations.cpp \
$$PWD/requestreferencesmessage.cpp \
$$PWD/registerunsavedfilesforeditormessage.cpp \
$$PWD/unregisterunsavedfilesforeditormessage.cpp \
$$PWD/updatetranslationunitsforeditormessage.cpp \
@@ -52,6 +53,7 @@ SOURCES += $$PWD/clangcodemodelserverinterface.cpp \
$$PWD/ipcserverinterface.cpp \
$$PWD/clangcodemodelconnectionclient.cpp \
$$PWD/documentannotationschangedmessage.cpp \
$$PWD/referencesmessage.cpp \
$$PWD/refactoringclientproxy.cpp \
$$PWD/sourcelocationscontainer.cpp \
$$PWD/sourcelocationcontainerv2.cpp \
@@ -112,6 +114,8 @@ HEADERS += \
$$PWD/sourcelocationcontainer.h \
$$PWD/fixitcontainer.h \
$$PWD/requestdocumentannotations.h \
$$PWD/referencesmessage.h \
$$PWD/requestreferencesmessage.h \
$$PWD/registerunsavedfilesforeditormessage.h \
$$PWD/unregisterunsavedfilesforeditormessage.h \
$$PWD/updatetranslationunitsforeditormessage.h \

View File

@@ -113,6 +113,9 @@ enum class MessageType : quint8 {
RequestDocumentAnnotationsMessage,
DocumentAnnotationsChangedMessage,
RequestReferencesMessage,
ReferencesMessage,
UpdateVisibleTranslationUnitsMessage,
CompleteCodeMessage,

View File

@@ -28,6 +28,7 @@
#include "cmbcodecompletedmessage.h"
#include "cmbechomessage.h"
#include "documentannotationschangedmessage.h"
#include "referencesmessage.h"
#include "messageenvelop.h"
#include "projectpartsdonotexistmessage.h"
#include "translationunitdoesnotexistmessage.h"
@@ -58,6 +59,9 @@ void ClangCodeModelClientInterface::dispatch(const MessageEnvelop &messageEnvelo
case MessageType::DocumentAnnotationsChangedMessage:
documentAnnotationsChanged(messageEnvelop.message<DocumentAnnotationsChangedMessage>());
break;
case MessageType::ReferencesMessage:
references(messageEnvelop.message<ReferencesMessage>());
break;
default:
qWarning() << "Unknown ClangCodeModelClientMessage";
}

View File

@@ -44,6 +44,8 @@ class RegisterUnsavedFilesForEditorMessage;
class UnregisterUnsavedFilesForEditorMessage;
class UpdateVisibleTranslationUnitsMessage;
class RequestDocumentAnnotationsMessage;
class RequestReferencesMessage;
class ReferencesMessage;
class DocumentAnnotationsChangedMessage;
class CMBIPC_EXPORT ClangCodeModelClientInterface : public IpcClientInterface
@@ -57,6 +59,7 @@ public:
virtual void translationUnitDoesNotExist(const TranslationUnitDoesNotExistMessage &message) = 0;
virtual void projectPartsDoNotExist(const ProjectPartsDoNotExistMessage &message) = 0;
virtual void documentAnnotationsChanged(const DocumentAnnotationsChangedMessage &message) = 0;
virtual void references(const ReferencesMessage &message) = 0;
};
} // namespace ClangBackEnd

View File

@@ -30,6 +30,7 @@
#include "cmbechomessage.h"
#include "cmbregistertranslationunitsforeditormessage.h"
#include "documentannotationschangedmessage.h"
#include "referencesmessage.h"
#include "clangcodemodelserverinterface.h"
#include "ipcserverinterface.h"
#include "messageenvelop.h"
@@ -101,6 +102,11 @@ void ClangCodeModelClientProxy::documentAnnotationsChanged(const DocumentAnnotat
m_writeMessageBlock.write(message);
}
void ClangCodeModelClientProxy::references(const ReferencesMessage &message)
{
m_writeMessageBlock.write(message);
}
void ClangCodeModelClientProxy::readMessages()
{
for (const MessageEnvelop &message : m_readMessageBlock.readAll())

View File

@@ -57,6 +57,7 @@ public:
void translationUnitDoesNotExist(const TranslationUnitDoesNotExistMessage &message) override;
void projectPartsDoNotExist(const ProjectPartsDoNotExistMessage &message) override;
void documentAnnotationsChanged(const DocumentAnnotationsChangedMessage &message) override;
void references(const ReferencesMessage &message) override;
void readMessages();

View File

@@ -33,6 +33,7 @@
#include "messageenvelop.h"
#include "registerunsavedfilesforeditormessage.h"
#include "requestdocumentannotations.h"
#include "requestreferencesmessage.h"
#include "unregisterunsavedfilesforeditormessage.h"
#include "updatetranslationunitsforeditormessage.h"
#include "updatevisibletranslationunitsmessage.h"
@@ -75,6 +76,9 @@ void ClangCodeModelServerInterface::dispatch(const MessageEnvelop &messageEnvelo
case MessageType::RequestDocumentAnnotationsMessage:
requestDocumentAnnotations(messageEnvelop.message<RequestDocumentAnnotationsMessage>());
break;
case MessageType::RequestReferencesMessage:
requestReferences(messageEnvelop.message<RequestReferencesMessage>());
break;
case MessageType::UpdateVisibleTranslationUnitsMessage:
updateVisibleTranslationUnits(messageEnvelop.message<UpdateVisibleTranslationUnitsMessage>());
break;

View File

@@ -48,6 +48,7 @@ public:
virtual void unregisterUnsavedFilesForEditor(const UnregisterUnsavedFilesForEditorMessage &message) = 0;
virtual void completeCode(const CompleteCodeMessage &message) = 0;
virtual void requestDocumentAnnotations(const RequestDocumentAnnotationsMessage &message) = 0;
virtual void requestReferences(const RequestReferencesMessage &message) = 0;
virtual void updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message) = 0;
};

View File

@@ -36,6 +36,7 @@
#include <messageenvelop.h>
#include <registerunsavedfilesforeditormessage.h>
#include <requestdocumentannotations.h>
#include <requestreferencesmessage.h>
#include <unregisterunsavedfilesforeditormessage.h>
#include <updatetranslationunitsforeditormessage.h>
#include <updatevisibletranslationunitsmessage.h>
@@ -116,6 +117,11 @@ void ClangCodeModelServerProxy::requestDocumentAnnotations(const RequestDocument
m_writeMessageBlock.write(message);
}
void ClangCodeModelServerProxy::requestReferences(const RequestReferencesMessage &message)
{
m_writeMessageBlock.write(message);
}
void ClangCodeModelServerProxy::updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message)
{
m_writeMessageBlock.write(message);

View File

@@ -59,6 +59,7 @@ public:
void unregisterUnsavedFilesForEditor(const UnregisterUnsavedFilesForEditorMessage &message) override;
void completeCode(const CompleteCodeMessage &message) override;
void requestDocumentAnnotations(const RequestDocumentAnnotationsMessage &message) override;
void requestReferences(const RequestReferencesMessage &message) override;
void updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message) override;
void readMessages();

View File

@@ -33,11 +33,13 @@
#include "cmbunregisterprojectsforeditormessage.h"
#include "cmbunregistertranslationunitsforeditormessage.h"
#include "documentannotationschangedmessage.h"
#include "referencesmessage.h"
#include "messageenvelop.h"
#include "messageenvelop.h"
#include "projectpartsdonotexistmessage.h"
#include "registerunsavedfilesforeditormessage.h"
#include "requestdocumentannotations.h"
#include "requestreferencesmessage.h"
#include "translationunitdoesnotexistmessage.h"
#include "unregisterunsavedfilesforeditormessage.h"
#include "updatetranslationunitsforeditormessage.h"
@@ -80,6 +82,9 @@ QDebug operator<<(QDebug debug, const MessageEnvelop &messageEnvelop)
case MessageType::RequestDocumentAnnotationsMessage:
qDebug() << messageEnvelop.message<RequestDocumentAnnotationsMessage>();
break;
case MessageType::RequestReferencesMessage:
qDebug() << messageEnvelop.message<RequestReferencesMessage>();
break;
case MessageType::UpdateVisibleTranslationUnitsMessage:
qDebug() << messageEnvelop.message<UpdateVisibleTranslationUnitsMessage>();
break;
@@ -92,6 +97,9 @@ QDebug operator<<(QDebug debug, const MessageEnvelop &messageEnvelop)
case MessageType::CodeCompletedMessage:
qDebug() << messageEnvelop.message<CodeCompletedMessage>();
break;
case MessageType::ReferencesMessage:
qDebug() << messageEnvelop.message<ReferencesMessage>();
break;
case MessageType::TranslationUnitDoesNotExistMessage:
qDebug() << messageEnvelop.message<TranslationUnitDoesNotExistMessage>();
break;

View File

@@ -0,0 +1,59 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "referencesmessage.h"
#include <QDebug>
#include <ostream>
namespace ClangBackEnd {
QDebug operator<<(QDebug debug, const ReferencesMessage &message)
{
debug.nospace() << "ReferencesMessage("
<< message.fileContainer()
<< ", " << message.m_ticketNumber
<< ", " << message.m_isLocalVariable
<< ", " << message.m_references;
debug.nospace() << ")";
return debug;
}
std::ostream &operator<<(std::ostream &os, const ReferencesMessage &message)
{
os << "("
<< message.m_fileContainer << ", "
<< message.m_ticketNumber << ", "
<< message.m_isLocalVariable << ", "
<< message.m_references << ", "
<< ")";
return os;
}
} // namespace ClangBackEnd

View File

@@ -0,0 +1,110 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "filecontainer.h"
#include "sourcerangecontainer.h"
#include <QDataStream>
#include <QVector>
namespace ClangBackEnd {
class ReferencesMessage
{
public:
ReferencesMessage() = default;
ReferencesMessage(const FileContainer &fileContainer,
const QVector<SourceRangeContainer> &references,
bool isLocalVariable,
quint64 ticketNumber)
: m_fileContainer(fileContainer)
, m_references(references)
, m_ticketNumber(ticketNumber)
, m_isLocalVariable(isLocalVariable)
{
}
const FileContainer &fileContainer() const
{
return m_fileContainer;
}
bool isLocalVariable() const
{
return m_isLocalVariable;
}
const QVector<SourceRangeContainer> &references() const
{
return m_references;
}
quint64 ticketNumber() const
{
return m_ticketNumber;
}
friend QDataStream &operator<<(QDataStream &out, const ReferencesMessage &message)
{
out << message.m_fileContainer;
out << message.m_isLocalVariable;
out << message.m_references;
out << message.m_ticketNumber;
return out;
}
friend QDataStream &operator>>(QDataStream &in, ReferencesMessage &message)
{
in >> message.m_fileContainer;
in >> message.m_isLocalVariable;
in >> message.m_references;
in >> message.m_ticketNumber;
return in;
}
friend bool operator==(const ReferencesMessage &first, const ReferencesMessage &second)
{
return first.m_ticketNumber == second.m_ticketNumber
&& first.m_isLocalVariable == second.m_isLocalVariable
&& first.m_fileContainer == second.m_fileContainer
&& first.m_references == second.m_references;
}
friend CMBIPC_EXPORT QDebug operator<<(QDebug debug, const ReferencesMessage &message);
friend std::ostream &operator<<(std::ostream &os, const ReferencesMessage &message);
private:
FileContainer m_fileContainer;
QVector<SourceRangeContainer> m_references;
quint64 m_ticketNumber = 0;
bool m_isLocalVariable = false;
};
DECLARE_MESSAGE(ReferencesMessage)
} // namespace ClangBackEnd

View File

@@ -0,0 +1,62 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "requestreferencesmessage.h"
#include <QDebug>
#include <ostream>
namespace ClangBackEnd {
quint64 RequestReferencesMessage::ticketCounter = 0;
QDebug operator<<(QDebug debug, const RequestReferencesMessage &message)
{
debug.nospace() << "RequestReferencesMessage(";
debug.nospace() << message.m_fileContainer << ", ";
debug.nospace() << message.m_ticketNumber << ", ";
debug.nospace() << message.m_line << ", ";
debug.nospace() << message.m_column << ", ";
debug.nospace() << ")";
return debug;
}
std::ostream &operator<<(std::ostream &os, const RequestReferencesMessage &message)
{
os << "("
<< message.m_fileContainer << ", "
<< message.m_ticketNumber << ", "
<< message.m_line << ", "
<< message.m_column << ", "
<< ")";
return os;
}
} // namespace ClangBackEnd

View File

@@ -0,0 +1,111 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "clangbackendipc_global.h"
#include "filecontainer.h"
#include <QDataStream>
namespace ClangBackEnd {
class RequestReferencesMessage
{
public:
RequestReferencesMessage() = default;
RequestReferencesMessage(const FileContainer &fileContainer,
quint32 line,
quint32 column)
: m_fileContainer(fileContainer)
, m_ticketNumber(++ticketCounter)
, m_line(line)
, m_column(column)
{
}
const FileContainer fileContainer() const
{
return m_fileContainer;
}
quint32 line() const
{
return m_line;
}
quint32 column() const
{
return m_column;
}
quint64 ticketNumber() const
{
return m_ticketNumber;
}
friend QDataStream &operator<<(QDataStream &out, const RequestReferencesMessage &message)
{
out << message.m_fileContainer;
out << message.m_ticketNumber;
out << message.m_line;
out << message.m_column;
return out;
}
friend QDataStream &operator>>(QDataStream &in, RequestReferencesMessage &message)
{
in >> message.m_fileContainer;
in >> message.m_ticketNumber;
in >> message.m_line;
in >> message.m_column;
return in;
}
friend bool operator==(const RequestReferencesMessage &first,
const RequestReferencesMessage &second)
{
return first.m_ticketNumber == second.m_ticketNumber
&& first.m_line == second.m_line
&& first.m_column == second.m_column
&& first.m_fileContainer == second.m_fileContainer;
}
friend CMBIPC_EXPORT QDebug operator<<(QDebug debug, const RequestReferencesMessage &message);
friend std::ostream &operator<<(std::ostream &os, const RequestReferencesMessage &message);
private:
FileContainer m_fileContainer;
quint64 m_ticketNumber = 0;
quint32 m_line = 0;
quint32 m_column = 0;
static CMBIPC_EXPORT quint64 ticketCounter;
};
DECLARE_MESSAGE(RequestReferencesMessage);
} // namespace ClangBackEnd

View File

@@ -58,8 +58,11 @@
#include <clangbackendipc/cmbunregistertranslationunitsforeditormessage.h>
#include <clangbackendipc/cmbunregisterprojectsforeditormessage.h>
#include <clangbackendipc/documentannotationschangedmessage.h>
#include <clangbackendipc/referencesmessage.h>
#include <clangbackendipc/requestreferencesmessage.h>
#include <clangbackendipc/registerunsavedfilesforeditormessage.h>
#include <clangbackendipc/requestdocumentannotations.h>
#include <clangbackendipc/requestreferencesmessage.h>
#include <clangbackendipc/filecontainer.h>
#include <clangbackendipc/projectpartsdonotexistmessage.h>
#include <clangbackendipc/translationunitdoesnotexistmessage.h>
@@ -74,6 +77,7 @@
#include <QElapsedTimer>
#include <QLoggingCategory>
#include <QProcess>
#include <QTextBlock>
static Q_LOGGING_CATEGORY(log, "qtc.clangcodemodel.ipc")
@@ -150,6 +154,21 @@ void IpcReceiver::deleteProcessorsOfEditorWidget(TextEditor::TextEditorWidget *t
}
}
QFuture<CppTools::CursorInfo> IpcReceiver::addExpectedReferencesMessage(quint64 ticket,
QTextDocument *textDocument)
{
QTC_CHECK(textDocument);
QTC_CHECK(!m_referencesTable.contains(ticket));
QFutureInterface<CppTools::CursorInfo> futureInterface;
futureInterface.reportStarted();
const ReferencesEntry entry{futureInterface, textDocument};
m_referencesTable.insert(ticket, entry);
return futureInterface.future();
}
bool IpcReceiver::isExpectingCodeCompletedMessage() const
{
return !m_assistProcessorsTable.isEmpty();
@@ -205,6 +224,56 @@ void IpcReceiver::documentAnnotationsChanged(const DocumentAnnotationsChangedMes
}
}
static
CppTools::CursorInfo::Range toCursorInfoRange(const QTextDocument &textDocument,
const SourceRangeContainer &sourceRange)
{
const SourceLocationContainer start = sourceRange.start();
const SourceLocationContainer end = sourceRange.end();
const unsigned length = end.column() - start.column();
const QTextBlock block = textDocument.findBlockByNumber(static_cast<int>(start.line()) - 1);
const int shift = ClangCodeModel::Utils::extraUtf8CharsShift(block.text(),
static_cast<int>(start.column()));
const uint column = start.column() - static_cast<uint>(shift);
return CppTools::CursorInfo::Range(start.line(), column, length);
}
static
CppTools::CursorInfo toCursorInfo(const QTextDocument &textDocument,
const ReferencesMessage &message)
{
CppTools::CursorInfo result;
const QVector<SourceRangeContainer> references = message.references();
result.areUseRangesForLocalVariable = message.isLocalVariable();
for (const SourceRangeContainer &reference : references)
result.useRanges.append(toCursorInfoRange(textDocument, reference));
result.useRanges.reserve(references.size());
return result;
}
void IpcReceiver::references(const ReferencesMessage &message)
{
qCDebug(log) << "<<< ReferencesMessage with"
<< message.references().size() << "references";
const quint64 ticket = message.ticketNumber();
const ReferencesEntry entry = m_referencesTable.take(ticket);
QFutureInterface<CppTools::CursorInfo> futureInterface = entry.futureInterface;
QTC_CHECK(futureInterface != QFutureInterface<CppTools::CursorInfo>());
if (futureInterface.isCanceled())
return; // A new request was issued making this one outdated.
QTC_CHECK(entry.textDocument);
futureInterface.reportResult(toCursorInfo(*entry.textDocument, message));
futureInterface.reportFinished();
}
class IpcSender : public IpcSenderInterface
{
public:
@@ -222,6 +291,7 @@ public:
void unregisterUnsavedFilesForEditor(const ClangBackEnd::UnregisterUnsavedFilesForEditorMessage &message) override;
void completeCode(const ClangBackEnd::CompleteCodeMessage &message) override;
void requestDocumentAnnotations(const ClangBackEnd::RequestDocumentAnnotationsMessage &message) override;
void requestReferences(const ClangBackEnd::RequestReferencesMessage &message) override;
void updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message) override;
private:
@@ -298,6 +368,13 @@ void IpcSender::requestDocumentAnnotations(const RequestDocumentAnnotationsMessa
m_connection.serverProxy().requestDocumentAnnotations(message);
}
void IpcSender::requestReferences(const RequestReferencesMessage &message)
{
QTC_CHECK(m_connection.isConnected());
qCDebug(log) << ">>>" << message;
m_connection.serverProxy().requestReferences(message);
}
void IpcSender::updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message)
{
QTC_CHECK(m_connection.isConnected());
@@ -318,6 +395,7 @@ public:
void unregisterUnsavedFilesForEditor(const ClangBackEnd::UnregisterUnsavedFilesForEditorMessage &) override {}
void completeCode(const ClangBackEnd::CompleteCodeMessage &) override {}
void requestDocumentAnnotations(const ClangBackEnd::RequestDocumentAnnotationsMessage &) override {}
void requestReferences(const ClangBackEnd::RequestReferencesMessage &) override {}
void updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &) override {}
};
@@ -603,6 +681,18 @@ void IpcCommunicator::requestDocumentAnnotations(const FileContainer &fileContai
m_ipcSender->requestDocumentAnnotations(message);
}
QFuture<CppTools::CursorInfo> IpcCommunicator::requestReferences(
const FileContainer &fileContainer,
quint32 line,
quint32 column,
QTextDocument *textDocument)
{
const RequestReferencesMessage message(fileContainer, line, column);
m_ipcSender->requestReferences(message);
return m_ipcReceiver.addExpectedReferencesMessage(message.ticketNumber(), textDocument);
}
void IpcCommunicator::updateTranslationUnitWithRevisionCheck(Core::IDocument *document)
{
const auto textDocument = qobject_cast<TextDocument*>(document);

View File

@@ -32,8 +32,10 @@
#include <clangbackendipc/clangcodemodelclientinterface.h>
#include <clangbackendipc/projectpartcontainer.h>
#include <QFuture>
#include <QObject>
#include <QSharedPointer>
#include <QTextDocument>
#include <QVector>
#include <functional>
@@ -43,6 +45,10 @@ class IEditor;
class IDocument;
}
namespace CppTools {
class CursorInfo;
}
namespace ClangBackEnd {
class DocumentAnnotationsChangedMessage;
}
@@ -72,6 +78,9 @@ public:
void deleteAndClearWaitingAssistProcessors();
void deleteProcessorsOfEditorWidget(TextEditor::TextEditorWidget *textEditorWidget);
QFuture<CppTools::CursorInfo> addExpectedReferencesMessage(quint64 ticket,
QTextDocument *textDocument);
bool isExpectingCodeCompletedMessage() const;
private:
@@ -80,6 +89,7 @@ private:
void codeCompleted(const ClangBackEnd::CodeCompletedMessage &message) override;
void documentAnnotationsChanged(const ClangBackEnd::DocumentAnnotationsChangedMessage &message) override;
void references(const ClangBackEnd::ReferencesMessage &message) override;
void translationUnitDoesNotExist(const ClangBackEnd::TranslationUnitDoesNotExistMessage &) override {}
void projectPartsDoNotExist(const ClangBackEnd::ProjectPartsDoNotExistMessage &) override {}
@@ -87,6 +97,12 @@ private:
private:
AliveHandler m_aliveHandler;
QHash<quint64, ClangCompletionAssistProcessor *> m_assistProcessorsTable;
struct ReferencesEntry {
QFutureInterface<CppTools::CursorInfo> futureInterface;
QTextDocument *textDocument = nullptr;
};
QHash<quint64, ReferencesEntry> m_referencesTable;
const bool m_printAliveMessage = false;
};
@@ -105,6 +121,7 @@ public:
virtual void unregisterUnsavedFilesForEditor(const ClangBackEnd::UnregisterUnsavedFilesForEditorMessage &message) = 0;
virtual void completeCode(const ClangBackEnd::CompleteCodeMessage &message) = 0;
virtual void requestDocumentAnnotations(const ClangBackEnd::RequestDocumentAnnotationsMessage &message) = 0;
virtual void requestReferences(const ClangBackEnd::RequestReferencesMessage &message) = 0;
virtual void updateVisibleTranslationUnits(const ClangBackEnd::UpdateVisibleTranslationUnitsMessage &message) = 0;
};
@@ -114,6 +131,7 @@ class IpcCommunicator : public QObject
public:
using Ptr = QSharedPointer<IpcCommunicator>;
using FileContainer = ClangBackEnd::FileContainer;
using FileContainers = QVector<ClangBackEnd::FileContainer>;
using ProjectPartContainers = QVector<ClangBackEnd::ProjectPartContainer>;
@@ -129,6 +147,9 @@ public:
void registerUnsavedFilesForEditor(const FileContainers &fileContainers);
void unregisterUnsavedFilesForEditor(const FileContainers &fileContainers);
void requestDocumentAnnotations(const ClangBackEnd::FileContainer &fileContainer);
QFuture<CppTools::CursorInfo> requestReferences(const FileContainer &fileContainer,
quint32 line,
quint32 column, QTextDocument *textDocument);
void completeCode(ClangCompletionAssistProcessor *assistProcessor, const QString &filePath,
quint32 line,
quint32 column,

View File

@@ -68,6 +68,7 @@ ClangEditorDocumentProcessor::ClangEditorDocumentProcessor(
IpcCommunicator &ipcCommunicator,
TextEditor::TextDocument *document)
: BaseEditorDocumentProcessor(document->document(), document->filePath().toString())
, m_document(*document)
, m_diagnosticManager(document)
, m_ipcCommunicator(ipcCommunicator)
, m_parser(new ClangEditorDocumentParser(document->filePath().toString()))
@@ -287,10 +288,48 @@ void ClangEditorDocumentProcessor::setParserConfig(
m_builtinProcessor.parser()->setConfiguration(config);
}
static bool isCursorOnIdentifier(const QTextCursor &textCursor)
{
QTextDocument *document = textCursor.document();
return CppTools::isValidIdentifierChar(document->characterAt(textCursor.position()));
}
static QFuture<CppTools::CursorInfo> defaultCursorInfoFuture()
{
QFutureInterface<CppTools::CursorInfo> futureInterface;
futureInterface.reportResult(CppTools::CursorInfo());
futureInterface.reportFinished();
return futureInterface.future();
}
static bool convertPosition(const QTextCursor &textCursor, int *line, int *column)
{
const bool converted = TextEditor::Convenience::convertPosition(textCursor.document(),
textCursor.position(),
line,
column);
QTC_CHECK(converted);
return converted;
}
QFuture<CppTools::CursorInfo>
ClangEditorDocumentProcessor::cursorInfo(const CppTools::CursorInfoParams &params)
{
return m_builtinProcessor.cursorInfo(params);
int line, column;
convertPosition(params.textCursor, &line, &column);
++column; // for 1-based columns
if (!isCursorOnIdentifier(params.textCursor))
return defaultCursorInfoFuture();
const QTextBlock block = params.textCursor.document()->findBlockByNumber(line - 1);
column += ClangCodeModel::Utils::extraUtf8CharsShift(block.text(), column);
return m_ipcCommunicator.requestReferences(simpleFileContainer(),
static_cast<quint32>(line),
static_cast<quint32>(column),
textDocument());
}
ClangBackEnd::FileContainer ClangEditorDocumentProcessor::fileContainerWithArguments() const
@@ -396,6 +435,15 @@ ClangEditorDocumentProcessor::creatorForHeaderErrorDiagnosticWidget(
};
}
ClangBackEnd::FileContainer ClangEditorDocumentProcessor::simpleFileContainer() const
{
Utf8String projectPartId;
if (m_projectPart)
projectPartId = m_projectPart->id();
return ClangBackEnd::FileContainer(filePath(), projectPartId, Utf8String(), false, revision());
}
static CppTools::ProjectPart projectPartForLanguageOption(CppTools::ProjectPart *projectPart)
{
if (projectPart)

View File

@@ -101,12 +101,14 @@ private:
void requestDocumentAnnotations(const QString &projectpartId);
HeaderErrorDiagnosticWidgetCreator creatorForHeaderErrorDiagnosticWidget(
const ClangBackEnd::DiagnosticContainer &firstHeaderErrorDiagnostic);
ClangBackEnd::FileContainer simpleFileContainer() const;
ClangBackEnd::FileContainer fileContainerWithArguments(CppTools::ProjectPart *projectPart) const;
ClangBackEnd::FileContainer fileContainerWithArgumentsAndDocumentContent(
CppTools::ProjectPart *projectPart) const;
ClangBackEnd::FileContainer fileContainerWithDocumentContent(const QString &projectpartId) const;
private:
TextEditor::TextDocument &m_document;
ClangDiagnosticManager m_diagnosticManager;
IpcCommunicator &m_ipcCommunicator;
QSharedPointer<ClangEditorDocumentParser> m_parser;

View File

@@ -325,6 +325,10 @@ QString toString(const RequestDocumentAnnotationsMessage &)
return QStringLiteral("RequestDocumentAnnotationsMessage\n");
}
QString toString(const RequestReferencesMessage &)
{
return QStringLiteral("RequestReferencesMessage\n");
}
QString toString(const UpdateVisibleTranslationUnitsMessage &)
{
@@ -364,6 +368,9 @@ public:
void requestDocumentAnnotations(const RequestDocumentAnnotationsMessage &message) override
{ senderLog.append(toString(message)); }
void requestReferences(const RequestReferencesMessage &message) override
{ senderLog.append(toString(message)); }
void updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message) override
{ senderLog.append(toString(message)); }

View File

@@ -105,7 +105,11 @@ void CppUseSelectionsUpdater::update(CallType callType)
m_runnerWatcher->setFuture(cppEditorDocument->cursorInfo(params));
} else { // synchronous case
QFuture<CursorInfo> future = cppEditorDocument->cursorInfo(params);
future.waitForFinished();
// QFuture::waitForFinished seems to block completely, not even
// allowing to process events from QLocalSocket.
while (!future.isFinished())
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
processResults(future.result());
}

View File

@@ -50,6 +50,8 @@ HEADERS += $$PWD/clangcodemodelserver.h \
$$PWD/clangsupportivetranslationunitinitializer.h \
$$PWD/clangparsesupportivetranslationunitjob.h \
$$PWD/clangreparsesupportivetranslationunitjob.h \
$$PWD/clangrequestreferencesjob.h \
$$PWD/clangreferencescollector.h
SOURCES += $$PWD/clangcodemodelserver.cpp \
$$PWD/codecompleter.cpp \
@@ -95,3 +97,5 @@ SOURCES += $$PWD/clangcodemodelserver.cpp \
$$PWD/clangsupportivetranslationunitinitializer.cpp \
$$PWD/clangparsesupportivetranslationunitjob.cpp \
$$PWD/clangreparsesupportivetranslationunitjob.cpp \
$$PWD/clangrequestreferencesjob.cpp \
$$PWD/clangreferencescollector.cpp

View File

@@ -44,6 +44,7 @@
#include <documentannotationschangedmessage.h>
#include <registerunsavedfilesforeditormessage.h>
#include <requestdocumentannotations.h>
#include <requestreferencesmessage.h>
#include <projectpartsdonotexistmessage.h>
#include <translationunitdoesnotexistmessage.h>
#include <unregisterunsavedfilesforeditormessage.h>
@@ -246,6 +247,31 @@ void ClangCodeModelServer::requestDocumentAnnotations(const RequestDocumentAnnot
}
}
void ClangCodeModelServer::requestReferences(const RequestReferencesMessage &message)
{
TIME_SCOPE_DURATION("ClangCodeModelServer::requestReferences");
qWarning() << "ClangCodeModelServer::requestReferences";
try {
const Document document = documents.document(message.fileContainer().filePath(),
message.fileContainer().projectPartId());
DocumentProcessor processor = documentProcessors().processor(document);
JobRequest jobRequest = processor.createJobRequest(JobRequest::Type::RequestReferences);
jobRequest.line = message.line();
jobRequest.column = message.column();
jobRequest.ticketNumber = message.ticketNumber();
// The unsaved files might get updater later, so take the current
// revision for the request.
jobRequest.documentRevision = message.fileContainer().documentRevision();
processor.addJob(jobRequest);
processor.process();
} catch (const std::exception &exception) {
qWarning() << "Error in ClangCodeModelServer::requestReferences:" << exception.what();
}
}
void ClangCodeModelServer::updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message)
{
TIME_SCOPE_DURATION("ClangCodeModelServer::updateVisibleTranslationUnits");

View File

@@ -58,6 +58,7 @@ public:
void completeCode(const CompleteCodeMessage &message) override;
void updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message) override;
void requestDocumentAnnotations(const RequestDocumentAnnotationsMessage &message) override;
void requestReferences(const RequestReferencesMessage &message) override;
public: // for tests
const Documents &documentsForTestOnly() const;

View File

@@ -32,6 +32,7 @@
#include "clangdocument.h"
#include "clangtranslationunits.h"
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
namespace ClangBackEnd {
@@ -121,6 +122,11 @@ bool DocumentProcessor::isSupportiveTranslationUnitInitialized() const
== SupportiveTranslationUnitInitializer::State::Initialized;
}
JobRequests &DocumentProcessor::queue()
{
return d->jobs.queue();
}
QList<Jobs::RunningJob> DocumentProcessor::runningJobs() const
{
return d->jobs.runningJobs();

View File

@@ -67,6 +67,7 @@ public:
public: // for tests
bool isSupportiveTranslationUnitInitialized() const;
JobRequests &queue();
QList<Jobs::RunningJob> runningJobs() const;
int queueSize() const;

View File

@@ -30,6 +30,7 @@
#include "clangparsesupportivetranslationunitjob.h"
#include "clangreparsesupportivetranslationunitjob.h"
#include "clangrequestdocumentannotationsjob.h"
#include "clangrequestreferencesjob.h"
#include "clangupdatedocumentannotationsjob.h"
Q_LOGGING_CATEGORY(jobsLog, "qtc.clangbackend.jobs");
@@ -51,6 +52,8 @@ IAsyncJob *IAsyncJob::create(JobRequest::Type type)
return new CompleteCodeJob();
case JobRequest::Type::RequestDocumentAnnotations:
return new RequestDocumentAnnotationsJob();
case JobRequest::Type::RequestReferences:
return new RequestReferencesJob();
}
return nullptr;

View File

@@ -197,6 +197,20 @@ JobRequests JobQueue::takeJobRequestsToRunNow()
if (isJobRunningForTranslationUnit(id))
continue;
if (request.conditions.testFlag(JobRequest::Condition::CurrentDocumentRevision)) {
if (document.isDirty()) {
// TODO: If the document is dirty due to a project update,
// references are processes later than ideal.
qWarning() << "Not choosing due to dirty document:" << request;
continue;
}
if (request.documentRevision != document.documentRevision()) {
qWarning() << "Not choosing due to revision mismatch:" << request;
continue;
}
}
translationUnitsScheduledForThisRun.insert(id);
jobsToRun += request;
i.remove();
@@ -237,7 +251,7 @@ void JobQueue::setIsJobRunningForJobRequestHandler(
m_isJobRunningForJobRequestHandler = isJobRunningHandler;
}
JobRequests JobQueue::queue() const
JobRequests &JobQueue::queue()
{
return m_queue;
}

View File

@@ -52,7 +52,7 @@ public:
const IsJobRunningForJobRequestHandler &isJobRunningHandler);
public: // for tests
JobRequests queue() const;
JobRequests &queue();
int size() const;
void prioritizeRequests();

View File

@@ -39,6 +39,7 @@ static const char *JobRequestTypeToText(JobRequest::Type type)
RETURN_TEXT_FOR_CASE(CreateInitialDocumentPreamble);
RETURN_TEXT_FOR_CASE(CompleteCode);
RETURN_TEXT_FOR_CASE(RequestDocumentAnnotations);
RETURN_TEXT_FOR_CASE(RequestReferences);
}
return "UnhandledJobRequestType";
@@ -90,6 +91,7 @@ bool JobRequest::operator==(const JobRequest &other) const
{
return type == other.type
&& expirationReasons == other.expirationReasons
&& conditions == other.conditions
&& filePath == other.filePath
&& projectPartId == other.projectPartId
@@ -108,6 +110,7 @@ JobRequest::ExpirationReasons JobRequest::expirationReasonsForType(Type type)
switch (type) {
case JobRequest::Type::UpdateDocumentAnnotations:
return JobRequest::ExpirationReasons(JobRequest::AnythingChanged);
case JobRequest::Type::RequestReferences:
case JobRequest::Type::RequestDocumentAnnotations:
return JobRequest::ExpirationReasons(JobRequest::DocumentClosed
|JobRequest::DocumentRevisionChanged);
@@ -121,4 +124,12 @@ JobRequest::ExpirationReasons JobRequest::expirationReasonsForType(Type type)
return JobRequest::ExpirationReasons(JobRequest::DocumentClosed);
}
JobRequest::Conditions JobRequest::conditionsForType(JobRequest::Type type)
{
if (type == JobRequest::Type::RequestReferences)
return JobRequest::Conditions(JobRequest::Condition::CurrentDocumentRevision);
return JobRequest::Conditions(JobRequest::Condition::NoCondition);
}
} // namespace ClangBackEnd

View File

@@ -52,8 +52,15 @@ public:
CompleteCode,
RequestDocumentAnnotations,
RequestReferences,
};
enum class Condition {
NoCondition,
CurrentDocumentRevision,
};
Q_DECLARE_FLAGS(Conditions, Condition)
enum ExpirationReason {
Never = 1 << 0,
@@ -71,6 +78,7 @@ public:
public:
static ExpirationReasons expirationReasonsForType(Type type);
static Conditions conditionsForType(Type type);
JobRequest();
@@ -80,6 +88,7 @@ public:
quint64 id = 0;
Type type;
ExpirationReasons expirationReasons;
Conditions conditions;
// General
Utf8String filePath;
@@ -89,7 +98,7 @@ public:
uint documentRevision = 0;
PreferredTranslationUnit preferredTranslationUnit = PreferredTranslationUnit::RecentlyParsed;
// For code completion
// Specific to some jobs
quint32 line = 0;
quint32 column = 0;
quint64 ticketNumber = 0;

View File

@@ -76,6 +76,7 @@ JobRequest Jobs::createJobRequest(const Document &document,
JobRequest jobRequest;
jobRequest.type = type;
jobRequest.expirationReasons = JobRequest::expirationReasonsForType(type);
jobRequest.conditions = JobRequest::conditionsForType(type);
jobRequest.filePath = document.filePath();
jobRequest.projectPartId = document.projectPart().id();
jobRequest.unsavedFilesChangeTimePoint = m_unsavedFiles.lastChangeTimePoint();
@@ -173,7 +174,7 @@ QList<Jobs::RunningJob> Jobs::runningJobs() const
return m_running.values();
}
JobRequests Jobs::queue() const
JobRequests &Jobs::queue()
{
return m_queue.queue();
}

View File

@@ -75,7 +75,7 @@ public:
public /*for tests*/:
QList<RunningJob> runningJobs() const;
JobRequests queue() const;
JobRequests &queue();
bool isJobRunningForTranslationUnit(const Utf8String &translationUnitId) const;
bool isJobRunningForJobRequest(const JobRequest &jobRequest) const;

View File

@@ -0,0 +1,253 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "clangreferencescollector.h"
#include "clangstring.h"
#include "cursor.h"
#include "sourcerange.h"
#include <clangbackendipc/sourcerangecontainer.h>
#include <utils/qtcassert.h>
#include <utf8string.h>
#include <QDebug>
namespace ClangBackEnd {
namespace {
class ReferencedCursor
{
public:
static ReferencedCursor find(const Cursor &cursor)
{
// Query the referenced cursor directly instead of first testing with cursor.isReference().
// cursor.isReference() reports false for e.g. CXCursor_DeclRefExpr or CXCursor_CallExpr
// although it returns a valid cursor.
const Cursor referenced = cursor.referenced();
if (referenced.isValid())
return handleReferenced(referenced);
const Cursor definition = cursor.definition();
if (definition.isValid())
return definition;
return cursor;
}
Utf8String usr() const
{
return cursor.unifiedSymbolResolution() + usrSuffix;
}
bool isLocalVariable() const
{
return cursor.isLocalVariable();
}
private:
ReferencedCursor(const Cursor &cursor, const Utf8String &usrSuffix = Utf8String())
: cursor(cursor)
, usrSuffix(usrSuffix)
{}
static ReferencedCursor handleReferenced(const Cursor &cursor)
{
if (cursor.kind() == CXCursor_OverloadedDeclRef) {
// e.g. Text cursor is on "App" of "using N::App;".
if (cursor.overloadedDeclarationsCount() >= 1)
return cursor.overloadedDeclaration(0);
}
if (cursor.isConstructorOrDestructor()) {
const Type type = cursor.type();
if (type.isValid()) {
const Cursor typeDeclaration = type.declaration();
if (typeDeclaration.isValid()) {
// A CXCursor_CallExpr like "new Foo" has a type of CXType_Record and its
// declaration is e.g. CXCursor_ClassDecl.
return typeDeclaration;
} else {
// A CXCursor_Constructor like "Foo();" has a type of CXType_FunctionProto
// and its type declaration is invalid, so use the semantic parent.
const Cursor parent = cursor.semanticParent();
if (parent.isValid())
return parent;
}
}
}
if (cursor.isFunctionLike() || cursor.isTemplateLike()) {
const Cursor parent = cursor.semanticParent();
const ClangString spelling = cursor.spelling();
return {parent, Utf8StringLiteral("_qtc_") + Utf8String(spelling)};
}
return cursor;
}
private:
Cursor cursor;
Utf8String usrSuffix;
};
class ReferencesCollector
{
public:
ReferencesCollector(CXTranslationUnit cxTranslationUnit);
~ReferencesCollector();
ReferencesResult collect(uint line, uint column) const;
private:
bool isWithinTokenRange(CXToken token, uint line, uint column) const;
bool pointsToIdentifier(uint line, uint column, unsigned *tokenIndex) const;
bool matchesIdentifier(const CXToken &token, const Utf8String &identifier) const;
bool checkToken(unsigned index, const Utf8String &identifier, const Utf8String &usr) const;
private:
CXTranslationUnit m_cxTranslationUnit = nullptr;
CXToken *m_cxTokens = nullptr;
uint m_cxTokenCount = 0;
QVector<CXCursor> m_cxCursors;
};
ReferencesCollector::ReferencesCollector(CXTranslationUnit cxTranslationUnit)
: m_cxTranslationUnit(cxTranslationUnit)
{
const CXSourceRange range
= clang_getCursorExtent(clang_getTranslationUnitCursor(m_cxTranslationUnit));
clang_tokenize(cxTranslationUnit, range, &m_cxTokens, &m_cxTokenCount);
m_cxCursors.resize(static_cast<int>(m_cxTokenCount));
clang_annotateTokens(cxTranslationUnit, m_cxTokens, m_cxTokenCount, m_cxCursors.data());
}
ReferencesCollector::~ReferencesCollector()
{
clang_disposeTokens(m_cxTranslationUnit, m_cxTokens, m_cxTokenCount);
}
bool ReferencesCollector::isWithinTokenRange(CXToken token, uint line, uint column) const
{
const CXSourceLocation location = clang_getTokenLocation(m_cxTranslationUnit, token);
uint candidateLine = 0;
uint candiateColumn = 0;
clang_getFileLocation(location, nullptr, &candidateLine, &candiateColumn, nullptr);
const SourceRange range = clang_getTokenExtent(m_cxTranslationUnit, token);
return range.contains(line, column);
}
bool ReferencesCollector::pointsToIdentifier(uint line, uint column, unsigned *tokenIndex) const
{
for (uint i = 0; i < m_cxTokenCount; ++i) {
const CXToken token = m_cxTokens[i];
if (clang_getTokenKind(token) == CXToken_Identifier
&& isWithinTokenRange(token, line, column)) {
*tokenIndex = i;
return true;
}
}
return false;
}
bool ReferencesCollector::matchesIdentifier(const CXToken &token,
const Utf8String &identifier) const
{
const CXTokenKind tokenKind = clang_getTokenKind(token);
if (tokenKind == CXToken_Identifier) {
const Utf8String candidateIdentifier
= ClangString(clang_getTokenSpelling(m_cxTranslationUnit, token));
return candidateIdentifier == identifier;
}
return false;
}
bool ReferencesCollector::checkToken(unsigned index, const Utf8String &identifier,
const Utf8String &usr) const
{
const CXToken token = m_cxTokens[index];
if (!matchesIdentifier(token, identifier))
return false;
{ // For debugging only
// const SourceRange range = clang_getTokenExtent(m_cxTranslationUnit, token);
// const uint line = range.start().line();
// const ClangString spellingCs = clang_getTokenSpelling(m_cxTranslationUnit, token);
// const Utf8String spelling = spellingCs;
// qWarning() << "ReferencesCollector::checkToken:" << line << spelling;
}
const Cursor currentCursor(m_cxCursors[static_cast<int>(index)]);
const ReferencedCursor candidate = ReferencedCursor::find(currentCursor);
return candidate.usr() == usr;
}
ReferencesResult ReferencesCollector::collect(uint line, uint column) const
{
ReferencesResult result;
unsigned index = 0;
if (!pointsToIdentifier(line, column, &index))
return result;
const Cursor cursorFromUser = m_cxCursors[static_cast<int>(index)];
const ReferencedCursor refCursor = ReferencedCursor::find(cursorFromUser);
const Utf8String usr = refCursor.usr();
if (usr.isEmpty())
return result;
const CXToken token = m_cxTokens[index];
const Utf8String identifier = ClangString(clang_getTokenSpelling(m_cxTranslationUnit, token));
for (uint i = 0; i < m_cxTokenCount; ++i) {
if (checkToken(i, identifier, usr)) {
const SourceRange range = clang_getTokenExtent(m_cxTranslationUnit, m_cxTokens[i]);
result.references.append(range);
}
}
result.isLocalVariable = refCursor.isLocalVariable();
return result;
}
} // anonymous namespace
ReferencesResult collectReferences(CXTranslationUnit cxTranslationUnit,
uint line,
uint column)
{
ReferencesCollector collector(cxTranslationUnit);
return collector.collect(line, column);
}
} // namespace ClangBackEnd

View File

@@ -0,0 +1,52 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <clangbackendipc/sourcerangecontainer.h>
#include <QVector>
#include <clang-c/Index.h>
namespace ClangBackEnd {
struct ReferencesResult {
bool isLocalVariable = false;
QVector<SourceRangeContainer> references;
bool operator==(const ReferencesResult &other) const
{
return isLocalVariable == other.isLocalVariable
&& references == other.references;
}
};
ReferencesResult collectReferences(CXTranslationUnit cxTranslationUnit,
uint line,
uint column);
} // namespace ClangBackEnd

View File

@@ -0,0 +1,83 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "clangrequestreferencesjob.h"
#include <clangbackendipc/clangbackendipcdebugutils.h>
#include <clangbackendipc/referencesmessage.h>
#include <clangbackendipc/clangcodemodelclientinterface.h>
#include <utils/qtcassert.h>
namespace ClangBackEnd {
static RequestReferencesJob::AsyncResult runAsyncHelper(const TranslationUnit &translationUnit,
quint32 line,
quint32 column)
{
TIME_SCOPE_DURATION("RequestReferencesJobRunner");
return translationUnit.references(line, column);
}
IAsyncJob::AsyncPrepareResult RequestReferencesJob::prepareAsyncRun()
{
const JobRequest jobRequest = context().jobRequest;
QTC_ASSERT(jobRequest.type == JobRequest::Type::RequestReferences,
return AsyncPrepareResult());
try {
m_pinnedDocument = context().documentForJobRequest();
m_pinnedFileContainer = m_pinnedDocument.fileContainer();
const TranslationUnit translationUnit
= m_pinnedDocument.translationUnit(jobRequest.preferredTranslationUnit);
const quint32 line = jobRequest.line;
const quint32 column = jobRequest.column;
setRunner([translationUnit, line, column]() {
return runAsyncHelper(translationUnit, line, column);
});
return AsyncPrepareResult{translationUnit.id()};
} catch (const std::exception &exception) {
qWarning() << "Error in RequestReferencesJob::prepareAsyncRun:" << exception.what();
return AsyncPrepareResult();
}
}
void RequestReferencesJob::finalizeAsyncRun()
{
if (!context().isOutdated()) {
const AsyncResult result = asyncResult();
const ReferencesMessage message(m_pinnedFileContainer,
result.references,
result.isLocalVariable,
context().jobRequest.ticketNumber);
context().client->references(message);
}
}
} // namespace ClangBackEnd

View File

@@ -0,0 +1,49 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "clangasyncjob.h"
#include "clangreferencescollector.h"
#include "clangdocument.h"
#include <clangbackendipc/sourcerangecontainer.h>
namespace ClangBackEnd {
class RequestReferencesJob : public AsyncJob<ReferencesResult>
{
public:
using AsyncResult = ReferencesResult;
AsyncPrepareResult prepareAsyncRun() override;
void finalizeAsyncRun() override;
private:
Document m_pinnedDocument;
FileContainer m_pinnedFileContainer;
};
} // namespace ClangBackEnd

View File

@@ -24,6 +24,8 @@
****************************************************************************/
#include "clangtranslationunit.h"
#include "clangreferencescollector.h"
#include "clangtranslationunitupdater.h"
#include <codecompleter.h>
@@ -122,6 +124,11 @@ void TranslationUnit::extractDocumentAnnotations(
skippedSourceRanges = this->skippedSourceRanges().toSourceRangeContainers();
}
ReferencesResult TranslationUnit::references(uint line, uint column) const
{
return collectReferences(m_cxTranslationUnit, line, column);
}
DiagnosticSet TranslationUnit::diagnostics() const
{
return DiagnosticSet(clang_getDiagnosticSetFromTU(m_cxTranslationUnit));

View File

@@ -40,6 +40,7 @@ class DiagnosticContainer;
class DiagnosticSet;
class HighlightingMarkContainer;
class HighlightingMarks;
class ReferencesResult;
class SkippedSourceRanges;
class SourceLocation;
class SourceRange;
@@ -83,6 +84,8 @@ public:
QVector<HighlightingMarkContainer> &highlightingMarks,
QVector<SourceRangeContainer> &skippedSourceRanges) const;
ReferencesResult references(uint line, uint column) const;
DiagnosticSet diagnostics() const;
SourceLocation sourceLocationAt(uint line, uint column) const;

View File

@@ -34,6 +34,11 @@
namespace ClangBackEnd {
bool Type::isValid() const
{
return cxType.kind != CXType_Invalid;
}
bool Type::isConstant() const
{
return clang_isConstQualifiedType(cxType);
@@ -74,6 +79,11 @@ bool Type::isOutputArgument() const
return (isPointer() || isLValueReference()) && !pointeeType().isConstant();
}
bool Type::isBuiltinType() const
{
return cxType.kind >= CXType_FirstBuiltin && cxType.kind <= CXType_LastBuiltin;
}
Utf8String Type::utf8Spelling() const
{
return ClangString(clang_getTypeSpelling(cxType));

View File

@@ -42,6 +42,8 @@ class Type
friend bool operator==(Type first, Type second);
public:
bool isValid() const;
bool isConstant() const;
bool isConstantReference();
bool isPointer() const;
@@ -50,6 +52,7 @@ public:
bool isLValueReference() const;
bool isReferencingConstant() const;
bool isOutputArgument() const;
bool isBuiltinType() const;
Utf8String utf8Spelling() const;
ClangString spelling() const;

View File

@@ -118,6 +118,46 @@ bool Cursor::isLocalVariable() const
}
}
bool Cursor::isReference() const
{
return clang_isReference(kind());
}
bool Cursor::isExpression() const
{
return clang_isExpression(kind());
}
bool Cursor::isFunctionLike() const
{
const CXCursorKind k = kind();
return k == CXCursor_FunctionDecl
|| k == CXCursor_CXXMethod
|| k == CXCursor_FunctionTemplate;
}
bool Cursor::isConstructorOrDestructor() const
{
const CXCursorKind k = kind();
return k == CXCursor_Constructor
|| k == CXCursor_Destructor;
}
bool Cursor::isTemplateLike() const
{
switch (kind()) {
case CXCursor_ClassTemplate:
case CXCursor_ClassTemplatePartialSpecialization:
return true;
case CXCursor_ClassDecl:
return specializedCursorTemplate().isValid();
default:
return false;
}
Q_UNREACHABLE();
}
bool Cursor::hasFinalFunctionAttribute() const
{
bool hasFinal = false;
@@ -208,6 +248,11 @@ Type Cursor::nonPointerTupe() const
return typeResult;
}
Cursor Cursor::specializedCursorTemplate() const
{
return clang_getSpecializedCursorTemplate(cxCursor);
}
SourceLocation Cursor::sourceLocation() const
{
return clang_getCursorLocation(cxCursor);

View File

@@ -62,6 +62,11 @@ public:
bool isCompoundType() const;
bool isDeclaration() const;
bool isLocalVariable() const;
bool isReference() const;
bool isExpression() const;
bool isFunctionLike() const;
bool isConstructorOrDestructor() const;
bool isTemplateLike() const;
bool hasFinalFunctionAttribute() const;
bool hasFinalClassAttribute() const;
bool isUnexposed() const;
@@ -95,6 +100,7 @@ public:
Cursor argument(int index) const;
unsigned overloadedDeclarationsCount() const;
Cursor overloadedDeclaration(unsigned index) const;
Cursor specializedCursorTemplate() const;
void collectOutputArgumentRangesTo(
std::vector<CXSourceRange> &outputArgumentRanges) const;

View File

@@ -29,6 +29,8 @@
#include <ostream>
#include <utils/qtcassert.h>
namespace ClangBackEnd {
SourceRange::SourceRange()
@@ -61,6 +63,17 @@ SourceLocation SourceRange::end() const
return SourceLocation(clang_getRangeEnd(cxSourceRange));
}
bool SourceRange::contains(unsigned line, unsigned column) const
{
const SourceLocation start_ = start();
const SourceLocation end_ = end();
return start_.line() <= line
&& start_.column() <= column
&& line <= end_.line()
&& column <= end_.column();
}
SourceRangeContainer SourceRange::toSourceRangeContainer() const
{
return SourceRangeContainer(start().toSourceLocationContainer(),

View File

@@ -49,6 +49,8 @@ public:
SourceLocation start() const;
SourceLocation end() const;
bool contains(unsigned line, unsigned column) const;
SourceRangeContainer toSourceRangeContainer() const;
operator CXSourceRange() const;

View File

@@ -35,7 +35,8 @@
#include <clangbackendipc/cmbunregistertranslationunitsforeditormessage.h>
#include <clangbackendipc/connectionserver.h>
#include <clangbackendipc/registerunsavedfilesforeditormessage.h>
#include <requestdocumentannotations.h>
#include <clangbackendipc/requestdocumentannotations.h>
#include <clangbackendipc/requestreferencesmessage.h>
#include <clangbackendipc/unregisterunsavedfilesforeditormessage.h>
#include <clangbackendipc/updatetranslationunitsforeditormessage.h>
#include <clangbackendipc/updatevisibletranslationunitsmessage.h>
@@ -102,6 +103,11 @@ void EchoClangCodeModelServer::requestDocumentAnnotations(const RequestDocumentA
echoMessage(message);
}
void EchoClangCodeModelServer::requestReferences(const RequestReferencesMessage &message)
{
echoMessage(message);
}
void EchoClangCodeModelServer::updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message)
{
echoMessage(message);

View File

@@ -43,6 +43,7 @@ public:
void unregisterUnsavedFilesForEditor(const UnregisterUnsavedFilesForEditorMessage &message) override;
void completeCode(const CompleteCodeMessage &message) override;
void requestDocumentAnnotations(const RequestDocumentAnnotationsMessage &message) override;
void requestReferences(const RequestReferencesMessage &message) override;
void updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message) override;
private:

View File

@@ -41,6 +41,9 @@
#include <cmbregistertranslationunitsforeditormessage.h>
#include <cmbunregisterprojectsforeditormessage.h>
#include <cmbunregistertranslationunitsforeditormessage.h>
#include <requestreferencesmessage.h>
#include <utils/algorithm.h>
#include <QCoreApplication>
#include <QFile>
@@ -128,6 +131,7 @@ protected:
void updateVisibilty(const Utf8String &currentEditor, const Utf8String &additionalVisibleEditor);
void requestDocumentAnnotations(const Utf8String &filePath);
void requestReferences(quint32 documentRevision = 0);
void completeCode(const Utf8String &filePath, uint line = 1, uint column = 1,
const Utf8String &projectPartId = Utf8String());
@@ -136,6 +140,8 @@ protected:
bool isSupportiveTranslationUnitInitialized(const Utf8String &filePath);
DocumentProcessor documentProcessorForFile(const Utf8String &filePath);
void expectDocumentAnnotationsChanged(int count);
void expectCompletion(const CodeCompletion &completion);
void expectCompletionFromFileA();
@@ -143,6 +149,7 @@ protected:
void expectCompletionFromFileAUnsavedMethodVersion1();
void expectCompletionFromFileAUnsavedMethodVersion2();
void expectNoCompletionWithUnsavedMethod();
void expectReferences();
void expectDocumentAnnotationsChangedForFileBWithSpecificHighlightingMark();
static const Utf8String unsavedContent(const QString &unsavedFilePath);
@@ -160,6 +167,7 @@ protected:
= QStringLiteral(TESTDATA_DIR) + QStringLiteral("/complete_extractor_function_unsaved_2.cpp");
const Utf8String filePathB = Utf8StringLiteral(TESTDATA_DIR"/complete_extractor_variable.cpp");
const Utf8String filePathC = Utf8StringLiteral(TESTDATA_DIR"/references.cpp");
const Utf8String aFilePath = Utf8StringLiteral("afile.cpp");
const Utf8String anExistingFilePath
@@ -185,6 +193,25 @@ TEST_F(ClangCodeModelServerSlowTest, RequestDocumentAnnotations)
requestDocumentAnnotations(filePathB);
}
TEST_F(ClangCodeModelServerSlowTest, RequestReferencesForCurrentDocumentRevision)
{
registerProjectAndFileAndWaitForFinished(filePathC);
expectReferences();
requestReferences();
}
TEST_F(ClangCodeModelServerSlowTest, RequestReferencesTakesRevisionFromMessage)
{
registerProjectAndFileAndWaitForFinished(filePathC);
requestReferences(/*documentRevision=*/ 99);
JobRequests &queue = documentProcessorForFile(filePathC).queue();
Utils::anyOf(queue, [](const JobRequest &request) { return request.documentRevision == 99; });
queue.clear(); // Avoid blocking
}
TEST_F(ClangCodeModelServerSlowTest, NoInitialDocumentAnnotationsForClosedDocument)
{
const int expectedDocumentAnnotationsChangedCount = 0;
@@ -462,7 +489,15 @@ bool ClangCodeModelServer::isSupportiveTranslationUnitInitialized(const Utf8Stri
return document.translationUnits().size() == 2
&& documentProcessor.hasSupportiveTranslationUnit()
&& documentProcessor.isSupportiveTranslationUnitInitialized();
&& documentProcessor.isSupportiveTranslationUnitInitialized();
}
DocumentProcessor ClangCodeModelServer::documentProcessorForFile(const Utf8String &filePath)
{
Document document = clangServer.documentsForTestOnly().document(filePath, projectPartId);
DocumentProcessor documentProcessor = clangServer.documentProcessors().processor(document);
return documentProcessor;
}
void ClangCodeModelServer::expectCompletion(const CodeCompletion &completion)
@@ -512,6 +547,20 @@ void ClangCodeModelServer::expectNoCompletionWithUnsavedMethod()
.Times(1);
}
void ClangCodeModelServer::expectReferences()
{
const QVector<ClangBackEnd::SourceRangeContainer> references{{
{filePathC, 3, 9},
{filePathC, 3, 12}
}};
EXPECT_CALL(mockClangCodeModelClient,
references(
Property(&ReferencesMessage::references,
Eq(references))))
.Times(1);
}
void ClangCodeModelServer::expectCompletionFromFileA()
{
const CodeCompletion completion(Utf8StringLiteral("Function"),
@@ -528,6 +577,15 @@ void ClangCodeModelServer::requestDocumentAnnotations(const Utf8String &filePath
clangServer.requestDocumentAnnotations(message);
}
void ClangCodeModelServer::requestReferences(quint32 documentRevision)
{
const FileContainer fileContainer{filePathC, projectPartId, Utf8StringVector(),
documentRevision};
const RequestReferencesMessage message{fileContainer, 3, 9};
clangServer.requestReferences(message);
}
void ClangCodeModelServer::expectDocumentAnnotationsChangedForFileBWithSpecificHighlightingMark()
{
HighlightingTypes types;

View File

@@ -65,6 +65,9 @@ protected:
JobRequest::Type type,
PreferredTranslationUnit preferredTranslationUnit
= PreferredTranslationUnit::RecentlyParsed) const;
JobRequest createJobRequestWithConditions(const Utf8String &filePath,
JobRequest::Type type,
JobRequest::Conditions conditions) const;
void updateDocumentRevision();
void updateUnsavedFiles();
@@ -410,6 +413,30 @@ TEST_F(JobQueue, RequestCompleteCodeOutdatableByDocumentRevisionChange)
ASSERT_THAT(jobsToStart.size(), Eq(0));
}
TEST_F(JobQueue, RequestReferencesRunsForCurrentDocumentRevision)
{
jobQueue.add( createJobRequestWithConditions(filePath1,
JobRequest::Type::RequestReferences,
JobRequest::Condition::CurrentDocumentRevision));
const JobRequests jobsToStart = jobQueue.processQueue();
ASSERT_THAT(jobsToStart.size(), Eq(1));
}
TEST_F(JobQueue, RequestReferencesOutdatableByDocumentClose)
{
jobQueue.add(createJobRequestWithConditions(filePath1,
JobRequest::Type::RequestReferences,
JobRequest::Condition::CurrentDocumentRevision));
removeDocument();
const JobRequests jobsToStart = jobQueue.processQueue();
ASSERT_THAT(jobsToStart.size(), Eq(0));
ASSERT_THAT(jobQueue.size(), Eq(0));
}
void JobQueue::SetUp()
{
projects.createOrUpdate({ProjectPartContainer(projectPartId)});
@@ -460,6 +487,18 @@ JobRequest JobQueue::createJobRequest(
return jobRequest;
}
JobRequest JobQueue::createJobRequestWithConditions(const Utf8String &filePath,
JobRequest::Type type,
JobRequest::Conditions conditions) const
{
JobRequest jobRequest = createJobRequest(filePath,
type,
PreferredTranslationUnit::RecentlyParsed);
jobRequest.conditions = conditions;
return jobRequest;
}
void JobQueue::updateDocumentRevision()
{
documents.update({FileContainer(filePath1, projectPartId, Utf8String(), true, 1)});

View File

@@ -56,7 +56,7 @@ protected:
void TearDown() override;
bool waitUntilAllJobsFinished(int timeOutInMs = 10000) const;
bool waitUntilJobChainFinished(int timeOutInMs = 10000) const;
bool waitUntilJobChainFinished(int timeOutInMs = 10000);
protected:
ClangBackEnd::ProjectParts projects;
@@ -134,7 +134,7 @@ bool Jobs::waitUntilAllJobsFinished(int timeOutInMs) const
return ProcessEventUtilities::processEventsUntilTrue(noJobsRunningAnymore, timeOutInMs);
}
bool Jobs::waitUntilJobChainFinished(int timeOutInMs) const
bool Jobs::waitUntilJobChainFinished(int timeOutInMs)
{
const auto noJobsRunningAnymore = [this]() {
return jobs.runningJobs().isEmpty() && jobs.queue().isEmpty();

View File

@@ -0,0 +1,479 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "googletest.h"
#include <clangbackendipc_global.h>
#include <clangreferencescollector.h>
#include <clangdocument.h>
#include <clangdocuments.h>
#include <clangtranslationunit.h>
#include <fixitcontainer.h>
#include <projectpart.h>
#include <projects.h>
#include <sourcelocationcontainer.h>
#include <sourcerangecontainer.h>
#include <unsavedfiles.h>
#include <utils/qtcassert.h>
#include <clang-c/Index.h>
using ::testing::Contains;
using ::testing::Not;
using ::testing::ContainerEq;
using ::testing::Eq;
using ::ClangBackEnd::ProjectPart;
using ::ClangBackEnd::SourceLocationContainer;
using ::ClangBackEnd::Document;
using ::ClangBackEnd::UnsavedFiles;
using ::ClangBackEnd::ReferencesResult;
using ::ClangBackEnd::SourceRangeContainer;
using References = QVector<SourceRangeContainer>;
namespace {
std::ostream &operator<<(std::ostream &os, const ReferencesResult &value)
{
os << "ReferencesResult(";
os << value.isLocalVariable << ", {";
for (const SourceRangeContainer &r : value.references) {
os << r.start().line() << ",";
os << r.start().column() << ",";
QTC_CHECK(r.start().line() == r.end().line());
os << r.end().column() - r.start().column() << ",";
}
os << "})";
return os;
}
struct Data {
Data()
{
document.parse();
}
ProjectPart projectPart{Utf8StringLiteral("projectPartId"), {Utf8StringLiteral("-std=c++14")}};
ClangBackEnd::ProjectParts projects;
ClangBackEnd::UnsavedFiles unsavedFiles;
ClangBackEnd::Documents documents{projects, unsavedFiles};
Document document{Utf8StringLiteral(TESTDATA_DIR"/references.cpp"),
projectPart,
Utf8StringVector(),
documents};
};
class ReferencesCollector : public ::testing::Test
{
protected:
ReferencesResult getReferences(uint line, uint column)
{
return d->document.translationUnit().references(line, column);
}
SourceLocationContainer createSourceLocation(uint line, uint column) const
{
return SourceLocationContainer(d->document.filePath(), line, column);
}
SourceRangeContainer createSourceRange(uint line, uint column, uint length) const
{
return SourceRangeContainer {
createSourceLocation(line, column),
createSourceLocation(line, column + length)
};
}
static void SetUpTestCase();
static void TearDownTestCase();
private:
static std::unique_ptr<Data> d;
};
// This test is not strictly needed as the plugin is supposed to put the cursor
// on the identifier start.
TEST_F(ReferencesCollector, CursorNotOnIdentifier)
{
const ReferencesResult expected { false, {}, };
const ReferencesResult actual = getReferences(3, 5);
ASSERT_THAT(actual, expected);
}
TEST_F(ReferencesCollector, LocalVariableWithSingleUse)
{
const ReferencesResult expected {
true,
{createSourceRange(3, 9, 3)},
};
const ReferencesResult actual = getReferences(3, 9);
ASSERT_THAT(actual, expected);
}
TEST_F(ReferencesCollector, LocalVariableWithTwoUses)
{
const ReferencesResult expected {
true,
{createSourceRange(10, 9, 3),
createSourceRange(11, 12, 3)},
};
const ReferencesResult actual = getReferences(10, 9);
ASSERT_THAT(actual, expected);
}
TEST_F(ReferencesCollector, ClassName)
{
const ReferencesResult expected {
false,
{createSourceRange(16, 7, 3),
createSourceRange(19, 5, 3)},
};
const ReferencesResult actual = getReferences(16, 7);
ASSERT_THAT(actual, expected);
}
TEST_F(ReferencesCollector, Namespace)
{
const ReferencesResult expected {
false,
{createSourceRange(24, 11, 1),
createSourceRange(25, 11, 1),
createSourceRange(26, 1, 1)},
};
const ReferencesResult actual = getReferences(24, 11);
ASSERT_THAT(actual, expected);
}
TEST_F(ReferencesCollector, ClassNameDeclaredWithUsing)
{
const ReferencesResult expected {
false,
{createSourceRange(30, 21, 3),
createSourceRange(31, 10, 3)},
};
const ReferencesResult actual = getReferences(30, 21);
ASSERT_THAT(actual, expected);
}
TEST_F(ReferencesCollector, ClassNameForwardDeclared)
{
const ReferencesResult expected {
false,
{createSourceRange(35, 7, 3),
createSourceRange(36, 14, 3)},
};
const ReferencesResult actual = getReferences(35, 7);
ASSERT_THAT(actual, expected);
}
TEST_F(ReferencesCollector, ClassNameAndNewExpression)
{
const ReferencesResult expected {
false,
{createSourceRange(40, 7, 3),
createSourceRange(43, 9, 3)},
};
const ReferencesResult actual = getReferences(40, 7);
ASSERT_THAT(actual, expected);
}
TEST_F(ReferencesCollector, InstantiatedTemplateObject)
{
const ReferencesResult expected {
true,
{createSourceRange(52, 19, 3),
createSourceRange(53, 5, 3)},
};
const ReferencesResult actual = getReferences(52, 19);
ASSERT_THAT(actual, expected);
}
TEST_F(ReferencesCollector, VariableInTemplate)
{
const ReferencesResult expected {
true,
{createSourceRange(62, 13, 3),
createSourceRange(63, 11, 3)},
};
const ReferencesResult actual = getReferences(62, 13);
ASSERT_THAT(actual, expected);
}
TEST_F(ReferencesCollector, MemberInTemplate)
{
const ReferencesResult expected {
false,
{createSourceRange(64, 16, 3),
createSourceRange(67, 7, 3)},
};
const ReferencesResult actual = getReferences(67, 7);
ASSERT_THAT(actual, expected);
}
TEST_F(ReferencesCollector, TemplateType)
{
const ReferencesResult expected {
false,
{createSourceRange(58, 19, 1),
createSourceRange(60, 5, 1),
createSourceRange(67, 5, 1)},
};
const ReferencesResult actual = getReferences(58, 19);
ASSERT_THAT(actual, expected);
}
TEST_F(ReferencesCollector, MemberAccessIntoTemplateParameter)
{
const ReferencesResult expected { false, {}, };
const ReferencesResult actual = getReferences(76, 9);
ASSERT_THAT(actual, expected);
}
TEST_F(ReferencesCollector, ConstructorAsType)
{
const ReferencesResult expected {
false,
{createSourceRange(81, 8, 3),
createSourceRange(82, 5, 3),
createSourceRange(83, 6, 3)},
};
const ReferencesResult actual = getReferences(82, 5);
ASSERT_THAT(actual, expected);
}
TEST_F(ReferencesCollector, OverloadsFreeStanding)
{
const ReferencesResult expected {
false,
{createSourceRange(88, 5, 3),
createSourceRange(89, 5, 3)},
};
const ReferencesResult actual = getReferences(88, 5);
ASSERT_THAT(actual, expected);
}
TEST_F(ReferencesCollector, OverloadsMemberFunctions)
{
const ReferencesResult expected {
false,
{createSourceRange(94, 9, 3),
createSourceRange(95, 9, 3)},
};
const ReferencesResult actual = getReferences(94, 9);
ASSERT_THAT(actual, expected);
}
TEST_F(ReferencesCollector, FunctionAndTemplateFunction)
{
const ReferencesResult expected {
false,
{createSourceRange(100, 26, 3),
createSourceRange(101, 5, 3)},
};
const ReferencesResult actual = getReferences(100, 26);
ASSERT_THAT(actual, expected);
}
TEST_F(ReferencesCollector, FunctionAndTemplateFunctionAsMember)
{
const ReferencesResult expected {
false,
{createSourceRange(106, 30, 3),
createSourceRange(107, 9, 3)},
};
const ReferencesResult actual = getReferences(106, 30);
ASSERT_THAT(actual, expected);
}
TEST_F(ReferencesCollector, EnumType)
{
const ReferencesResult expected {
false,
{createSourceRange(112, 6, 2),
createSourceRange(113, 8, 2)},
};
const ReferencesResult actual = getReferences(112, 6);
ASSERT_THAT(actual, expected);
}
TEST_F(ReferencesCollector, LambdaCapturedObject)
{
const ReferencesResult expected {
true,
{createSourceRange(122, 15, 3),
createSourceRange(122, 33, 3)},
};
const ReferencesResult actual = getReferences(122, 15);
ASSERT_THAT(actual, expected);
}
//// Disabled because it looks like the lambda initializer is not yet exposed by libclang.
TEST_F(ReferencesCollector, DISABLED_LambdaCaptureInitializer)
{
const ReferencesResult expected {
true,
{createSourceRange(121, 19, 3),
createSourceRange(122, 19, 3)},
};
const ReferencesResult actual = getReferences(122, 19);
ASSERT_THAT(actual, expected);
}
TEST_F(ReferencesCollector, TemplateSpecialization)
{
const ReferencesResult expected {
false,
{createSourceRange(127, 25, 3),
createSourceRange(128, 25, 3),
createSourceRange(129, 18, 3)},
};
const ReferencesResult actual = getReferences(127, 25);
ASSERT_THAT(actual, expected);
}
TEST_F(ReferencesCollector, TemplateDependentName)
{
const ReferencesResult expected {
false,
{createSourceRange(133, 34, 3)},
};
const ReferencesResult actual = getReferences(133, 34);
ASSERT_THAT(actual, expected);
}
TEST_F(ReferencesCollector, FunctionCallAndDefinition)
{
const ReferencesResult expected {
false,
{createSourceRange(140, 5, 3),
createSourceRange(142, 25, 3)},
};
const ReferencesResult actual = getReferences(140, 5);
ASSERT_THAT(actual, expected);
}
TEST_F(ReferencesCollector, ObjectLikeMacro)
{
const ReferencesResult expected {
false,
{createSourceRange(147, 9, 3),
createSourceRange(150, 12, 3)},
};
const ReferencesResult actual = getReferences(147, 9);
ASSERT_THAT(actual, expected);
}
TEST_F(ReferencesCollector, FunctionLikeMacro)
{
const ReferencesResult expected {
false,
{createSourceRange(155, 9, 3),
createSourceRange(158, 12, 3)},
};
const ReferencesResult actual = getReferences(155, 9);
ASSERT_THAT(actual, expected);
}
TEST_F(ReferencesCollector, ArgumentToFunctionLikeMacro)
{
const ReferencesResult expected {
true,
{createSourceRange(156, 27, 3),
createSourceRange(158, 16, 3)},
};
const ReferencesResult actual = getReferences(156, 27);
ASSERT_THAT(actual, expected);
}
std::unique_ptr<Data> ReferencesCollector::d;
void ReferencesCollector::SetUpTestCase()
{
d.reset(new Data);
}
void ReferencesCollector::TearDownTestCase()
{
d.reset();
}
} // anonymous namespace

View File

@@ -0,0 +1,114 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "clangasyncjob-base.h"
#include <clangrequestreferencesjob.h>
using namespace ClangBackEnd;
using testing::_;
using testing::Eq;
using testing::Property;
namespace {
class RequestReferencesJob : public ClangAsyncJobTest
{
protected:
void SetUp() override { BaseSetUp(JobRequest::Type::RequestReferences, job); }
protected:
ClangBackEnd::RequestReferencesJob job;
};
TEST_F(RequestReferencesJob, PrepareAsyncRun)
{
job.setContext(jobContext);
ASSERT_TRUE(job.prepareAsyncRun());
}
TEST_F(RequestReferencesJob, RunAsync)
{
job.setContext(jobContext);
job.prepareAsyncRun();
job.runAsync();
ASSERT_TRUE(waitUntilJobFinished(job));
}
TEST_F(RequestReferencesJob, SendReferences)
{
job.setContext(jobContextWithMockClient);
job.prepareAsyncRun();
EXPECT_CALL(mockIpcClient, references(_)).Times(1);
job.runAsync();
ASSERT_TRUE(waitUntilJobFinished(job));
}
TEST_F(RequestReferencesJob, ForwardTicketNumber)
{
jobRequest.ticketNumber = static_cast<quint64>(99);
jobContextWithMockClient = JobContext(jobRequest, &documents, &unsavedFiles, &mockIpcClient);
job.setContext(jobContextWithMockClient);
job.prepareAsyncRun();
EXPECT_CALL(mockIpcClient,
references(Property(&ReferencesMessage::ticketNumber, Eq(99))))
.Times(1);
job.runAsync();
ASSERT_TRUE(waitUntilJobFinished(job));
}
TEST_F(RequestReferencesJob, DontSendReferencesIfDocumentWasClosed)
{
job.setContext(jobContextWithMockClient);
job.prepareAsyncRun();
EXPECT_CALL(mockIpcClient, references(_)).Times(0);
job.runAsync();
documents.remove({FileContainer{filePath, projectPartId}});
ASSERT_TRUE(waitUntilJobFinished(job));
}
TEST_F(RequestReferencesJob, DontSendReferencesIfDocumentRevisionChanged)
{
job.setContext(jobContextWithMockClient);
job.prepareAsyncRun();
EXPECT_CALL(mockIpcClient, references(_)).Times(0);
job.runAsync();
documents.update({FileContainer(filePath, projectPartId, Utf8String(), true, 99)});
ASSERT_TRUE(waitUntilJobFinished(job));
}
} // anonymous

View File

@@ -45,6 +45,7 @@
#include <readmessageblock.h>
#include <registerunsavedfilesforeditormessage.h>
#include <requestdocumentannotations.h>
#include <requestreferencesmessage.h>
#include <translationunitdoesnotexistmessage.h>
#include <unregisterunsavedfilesforeditormessage.h>
#include <updatetranslationunitsforeditormessage.h>

View File

@@ -0,0 +1,159 @@
void variableSingleReference()
{
int foo;
}
int variableMultipleReferences()
{
int foo = 0;
return foo;
}
class Foo {};
void bla()
{
Foo foo;
}
namespace N { class Bar {}; }
namespace N { class Baz {}; }
N::Bar bar;
namespace G { class App {}; }
using G::App;
class Hoo;
void f(const Hoo &);
class Moo {};
void x()
{
new Moo;
}
class Element {};
template<typename T> struct Wrap { T member; };
void g()
{
Wrap<Element> con;
con.member;
}
template<typename T>
struct Wrapper {
T f()
{
int foo;
++foo;
return mem;
}
T mem;
};
template<typename T>
void f()
{
T mem;
mem.foo();
}
struct Woo {
Woo();
~Woo();
};
int muu();
int muu(int);
struct Doo {
int muu();
int muu(int);
};
template<typename T> int tuu();
int tuu(int);
struct Xoo {
template<typename T> int tuu();
int tuu(int);
};
enum ET { E1 };
bool e(ET e)
{
return e == E1;
}
struct LData { int member; };
void lambda(LData foo) {
auto l = [bar=foo] { return bar.member; };
}
template<class T> class Coo;
template<class T> class Coo<T*>;
template<> class Coo<int>;
template<typename T> typename T::foo n()
{
typename T::bla hello;
}
int rec(int n = 100)
{
return n == 0 ? 0 : rec(--n);
}
#define FOO 3
int objectLikeMacro()
{
return FOO;
}
#define BAR(x) x
int functionLikeMacro(int foo)
{
return BAR(foo);
}

View File

@@ -38,4 +38,5 @@ public:
void translationUnitDoesNotExist(const ClangBackEnd::TranslationUnitDoesNotExistMessage &) override {}
void projectPartsDoNotExist(const ClangBackEnd::ProjectPartsDoNotExistMessage &) override {}
void documentAnnotationsChanged(const ClangBackEnd::DocumentAnnotationsChangedMessage &) override {}
void references(const ClangBackEnd::ReferencesMessage &) override {}
};

View File

@@ -31,6 +31,7 @@
#include <clangbackendipc/cmbcodecompletedmessage.h>
#include <clangbackendipc/cmbechomessage.h>
#include <clangbackendipc/documentannotationschangedmessage.h>
#include <clangbackendipc/referencesmessage.h>
#include <clangbackendipc/projectpartsdonotexistmessage.h>
#include <clangbackendipc/translationunitdoesnotexistmessage.h>
#include <clangbackendipc/updatetranslationunitsforeditormessage.h>
@@ -51,4 +52,6 @@ public:
void(const ClangBackEnd::ProjectPartsDoNotExistMessage &message));
MOCK_METHOD1(documentAnnotationsChanged,
void(const ClangBackEnd::DocumentAnnotationsChangedMessage &message));
MOCK_METHOD1(references,
void(const ClangBackEnd::ReferencesMessage &message));
};

View File

@@ -54,6 +54,8 @@ public:
void(const ClangBackEnd::CompleteCodeMessage &message));
MOCK_METHOD1(requestDocumentAnnotations,
void(const ClangBackEnd::RequestDocumentAnnotationsMessage &message));
MOCK_METHOD1(requestReferences,
void(const ClangBackEnd::RequestReferencesMessage &message));
MOCK_METHOD1(updateVisibleTranslationUnits,
void(const ClangBackEnd::UpdateVisibleTranslationUnitsMessage &message));
};

View File

@@ -36,6 +36,8 @@
#include <highlightingmarkcontainer.h>
#include <messageenvelop.h>
#include <requestdocumentannotations.h>
#include <requestreferencesmessage.h>
#include <referencesmessage.h>
#include <readmessageblock.h>
#include <registerunsavedfilesforeditormessage.h>
#include <unregisterunsavedfilesforeditormessage.h>
@@ -206,6 +208,21 @@ TEST_F(ReadAndWriteMessageBlock, CompareRequestDocumentAnnotations)
CompareMessage(ClangBackEnd::RequestDocumentAnnotationsMessage(fileContainer));
}
TEST_F(ReadAndWriteMessageBlock, CompareRequestReferences)
{
CompareMessage(ClangBackEnd::RequestReferencesMessage{fileContainer, 13, 37});
}
TEST_F(ReadAndWriteMessageBlock, CompareReferences)
{
const QVector<ClangBackEnd::SourceRangeContainer> references{
true,
{{fileContainer.filePath(), 12, 34},
{fileContainer.filePath(), 56, 78}}
};
CompareMessage(ClangBackEnd::ReferencesMessage(fileContainer, references, true, 1));
}
TEST_F(ReadAndWriteMessageBlock, GetInvalidMessageForAPartialBuffer)
{
writeCodeCompletedMessage();

View File

@@ -85,6 +85,8 @@ SOURCES += \
clangjobqueue-test.cpp \
clangjobs-test.cpp \
clangrequestdocumentannotationsjob-test.cpp \
clangrequestreferencesjob-test.cpp \
clangreferencescollector-test.cpp \
clangstring-test.cpp \
clangtranslationunit-test.cpp \
clangtranslationunits-test.cpp \