aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNikolai Kosjar <nikolai.kosjar@qt.io>2017-06-09 12:19:09 +0200
committerNikolai Kosjar <nikolai.kosjar@qt.io>2017-06-14 12:49:05 +0000
commitca72c29462d9caae5e6095f8361f843758c5972c (patch)
tree21a39edf9b3fb1ab5090a83076e7483dc425d6fa
parent9de9da742333d6114b337abf4b64b15aa23afa9f (diff)
Clang: Provide highlighting for identifier under cursor
Change-Id: I80ffe23cbcc84ab7323124581d9dd6afbe974fd0 Reviewed-by: Marco Bubke <marco.bubke@qt.io>
-rw-r--r--src/libs/clangbackendipc/clangbackendipc-lib.pri4
-rw-r--r--src/libs/clangbackendipc/clangbackendipc_global.h3
-rw-r--r--src/libs/clangbackendipc/clangcodemodelclientinterface.cpp4
-rw-r--r--src/libs/clangbackendipc/clangcodemodelclientinterface.h3
-rw-r--r--src/libs/clangbackendipc/clangcodemodelclientproxy.cpp6
-rw-r--r--src/libs/clangbackendipc/clangcodemodelclientproxy.h1
-rw-r--r--src/libs/clangbackendipc/clangcodemodelserverinterface.cpp4
-rw-r--r--src/libs/clangbackendipc/clangcodemodelserverinterface.h1
-rw-r--r--src/libs/clangbackendipc/clangcodemodelserverproxy.cpp6
-rw-r--r--src/libs/clangbackendipc/clangcodemodelserverproxy.h1
-rw-r--r--src/libs/clangbackendipc/messageenvelop.cpp8
-rw-r--r--src/libs/clangbackendipc/referencesmessage.cpp59
-rw-r--r--src/libs/clangbackendipc/referencesmessage.h110
-rw-r--r--src/libs/clangbackendipc/requestreferencesmessage.cpp62
-rw-r--r--src/libs/clangbackendipc/requestreferencesmessage.h111
-rw-r--r--src/plugins/clangcodemodel/clangbackendipcintegration.cpp90
-rw-r--r--src/plugins/clangcodemodel/clangbackendipcintegration.h21
-rw-r--r--src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp50
-rw-r--r--src/plugins/clangcodemodel/clangeditordocumentprocessor.h2
-rw-r--r--src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp7
-rw-r--r--src/plugins/cppeditor/cppuseselectionsupdater.cpp6
-rw-r--r--src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri4
-rw-r--r--src/tools/clangbackend/ipcsource/clangcodemodelserver.cpp26
-rw-r--r--src/tools/clangbackend/ipcsource/clangcodemodelserver.h1
-rw-r--r--src/tools/clangbackend/ipcsource/clangdocumentprocessor.cpp6
-rw-r--r--src/tools/clangbackend/ipcsource/clangdocumentprocessor.h1
-rw-r--r--src/tools/clangbackend/ipcsource/clangiasyncjob.cpp3
-rw-r--r--src/tools/clangbackend/ipcsource/clangjobqueue.cpp16
-rw-r--r--src/tools/clangbackend/ipcsource/clangjobqueue.h2
-rw-r--r--src/tools/clangbackend/ipcsource/clangjobrequest.cpp11
-rw-r--r--src/tools/clangbackend/ipcsource/clangjobrequest.h11
-rw-r--r--src/tools/clangbackend/ipcsource/clangjobs.cpp3
-rw-r--r--src/tools/clangbackend/ipcsource/clangjobs.h2
-rw-r--r--src/tools/clangbackend/ipcsource/clangreferencescollector.cpp253
-rw-r--r--src/tools/clangbackend/ipcsource/clangreferencescollector.h52
-rw-r--r--src/tools/clangbackend/ipcsource/clangrequestreferencesjob.cpp83
-rw-r--r--src/tools/clangbackend/ipcsource/clangrequestreferencesjob.h49
-rw-r--r--src/tools/clangbackend/ipcsource/clangtranslationunit.cpp7
-rw-r--r--src/tools/clangbackend/ipcsource/clangtranslationunit.h3
-rw-r--r--src/tools/clangbackend/ipcsource/clangtype.cpp10
-rw-r--r--src/tools/clangbackend/ipcsource/clangtype.h3
-rw-r--r--src/tools/clangbackend/ipcsource/cursor.cpp45
-rw-r--r--src/tools/clangbackend/ipcsource/cursor.h6
-rw-r--r--src/tools/clangbackend/ipcsource/sourcerange.cpp13
-rw-r--r--src/tools/clangbackend/ipcsource/sourcerange.h2
-rw-r--r--tests/unit/echoserver/echoclangcodemodelserver.cpp8
-rw-r--r--tests/unit/echoserver/echoclangcodemodelserver.h1
-rw-r--r--tests/unit/unittest/clangipcserver-test.cpp60
-rw-r--r--tests/unit/unittest/clangjobqueue-test.cpp39
-rw-r--r--tests/unit/unittest/clangjobs-test.cpp4
-rw-r--r--tests/unit/unittest/clangreferencescollector-test.cpp479
-rw-r--r--tests/unit/unittest/clangrequestreferencesjob-test.cpp114
-rw-r--r--tests/unit/unittest/clientserverinprocess-test.cpp1
-rw-r--r--tests/unit/unittest/data/references.cpp159
-rw-r--r--tests/unit/unittest/dummyclangipcclient.h1
-rw-r--r--tests/unit/unittest/mockclangcodemodelclient.h3
-rw-r--r--tests/unit/unittest/mockclangcodemodelserver.h2
-rw-r--r--tests/unit/unittest/readandwritemessageblock-test.cpp17
-rw-r--r--tests/unit/unittest/unittest.pro2
59 files changed, 2050 insertions, 11 deletions
diff --git a/src/libs/clangbackendipc/clangbackendipc-lib.pri b/src/libs/clangbackendipc/clangbackendipc-lib.pri
index e6d44d4bcc7..084c2e8b3a0 100644
--- a/src/libs/clangbackendipc/clangbackendipc-lib.pri
+++ b/src/libs/clangbackendipc/clangbackendipc-lib.pri
@@ -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 \
diff --git a/src/libs/clangbackendipc/clangbackendipc_global.h b/src/libs/clangbackendipc/clangbackendipc_global.h
index 1392840fab1..8f7f08a6eeb 100644
--- a/src/libs/clangbackendipc/clangbackendipc_global.h
+++ b/src/libs/clangbackendipc/clangbackendipc_global.h
@@ -113,6 +113,9 @@ enum class MessageType : quint8 {
RequestDocumentAnnotationsMessage,
DocumentAnnotationsChangedMessage,
+ RequestReferencesMessage,
+ ReferencesMessage,
+
UpdateVisibleTranslationUnitsMessage,
CompleteCodeMessage,
diff --git a/src/libs/clangbackendipc/clangcodemodelclientinterface.cpp b/src/libs/clangbackendipc/clangcodemodelclientinterface.cpp
index a817e2f4146..d01cf18bc05 100644
--- a/src/libs/clangbackendipc/clangcodemodelclientinterface.cpp
+++ b/src/libs/clangbackendipc/clangcodemodelclientinterface.cpp
@@ -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";
}
diff --git a/src/libs/clangbackendipc/clangcodemodelclientinterface.h b/src/libs/clangbackendipc/clangcodemodelclientinterface.h
index e532da8b503..c451b4a4c93 100644
--- a/src/libs/clangbackendipc/clangcodemodelclientinterface.h
+++ b/src/libs/clangbackendipc/clangcodemodelclientinterface.h
@@ -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
diff --git a/src/libs/clangbackendipc/clangcodemodelclientproxy.cpp b/src/libs/clangbackendipc/clangcodemodelclientproxy.cpp
index 953aa49be09..70833f0b77f 100644
--- a/src/libs/clangbackendipc/clangcodemodelclientproxy.cpp
+++ b/src/libs/clangbackendipc/clangcodemodelclientproxy.cpp
@@ -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())
diff --git a/src/libs/clangbackendipc/clangcodemodelclientproxy.h b/src/libs/clangbackendipc/clangcodemodelclientproxy.h
index 005fe30dfa2..689f9684768 100644
--- a/src/libs/clangbackendipc/clangcodemodelclientproxy.h
+++ b/src/libs/clangbackendipc/clangcodemodelclientproxy.h
@@ -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();
diff --git a/src/libs/clangbackendipc/clangcodemodelserverinterface.cpp b/src/libs/clangbackendipc/clangcodemodelserverinterface.cpp
index aa94132b8a4..a2c501be3d1 100644
--- a/src/libs/clangbackendipc/clangcodemodelserverinterface.cpp
+++ b/src/libs/clangbackendipc/clangcodemodelserverinterface.cpp
@@ -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;
diff --git a/src/libs/clangbackendipc/clangcodemodelserverinterface.h b/src/libs/clangbackendipc/clangcodemodelserverinterface.h
index 0e0086da4f3..e25b9ef66b5 100644
--- a/src/libs/clangbackendipc/clangcodemodelserverinterface.h
+++ b/src/libs/clangbackendipc/clangcodemodelserverinterface.h
@@ -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;
};
diff --git a/src/libs/clangbackendipc/clangcodemodelserverproxy.cpp b/src/libs/clangbackendipc/clangcodemodelserverproxy.cpp
index aaf91927352..f743f8342aa 100644
--- a/src/libs/clangbackendipc/clangcodemodelserverproxy.cpp
+++ b/src/libs/clangbackendipc/clangcodemodelserverproxy.cpp
@@ -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);
diff --git a/src/libs/clangbackendipc/clangcodemodelserverproxy.h b/src/libs/clangbackendipc/clangcodemodelserverproxy.h
index 57421a19074..a854225ffc0 100644
--- a/src/libs/clangbackendipc/clangcodemodelserverproxy.h
+++ b/src/libs/clangbackendipc/clangcodemodelserverproxy.h
@@ -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();
diff --git a/src/libs/clangbackendipc/messageenvelop.cpp b/src/libs/clangbackendipc/messageenvelop.cpp
index 5922f26642d..c528dcd69ab 100644
--- a/src/libs/clangbackendipc/messageenvelop.cpp
+++ b/src/libs/clangbackendipc/messageenvelop.cpp
@@ -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;
diff --git a/src/libs/clangbackendipc/referencesmessage.cpp b/src/libs/clangbackendipc/referencesmessage.cpp
new file mode 100644
index 00000000000..8b994a7d7cf
--- /dev/null
+++ b/src/libs/clangbackendipc/referencesmessage.cpp
@@ -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
diff --git a/src/libs/clangbackendipc/referencesmessage.h b/src/libs/clangbackendipc/referencesmessage.h
new file mode 100644
index 00000000000..5d0b4d292a1
--- /dev/null
+++ b/src/libs/clangbackendipc/referencesmessage.h
@@ -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
diff --git a/src/libs/clangbackendipc/requestreferencesmessage.cpp b/src/libs/clangbackendipc/requestreferencesmessage.cpp
new file mode 100644
index 00000000000..0527db59d6e
--- /dev/null
+++ b/src/libs/clangbackendipc/requestreferencesmessage.cpp
@@ -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
diff --git a/src/libs/clangbackendipc/requestreferencesmessage.h b/src/libs/clangbackendipc/requestreferencesmessage.h
new file mode 100644
index 00000000000..8d76c1c5afd
--- /dev/null
+++ b/src/libs/clangbackendipc/requestreferencesmessage.h
@@ -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
diff --git a/src/plugins/clangcodemodel/clangbackendipcintegration.cpp b/src/plugins/clangcodemodel/clangbackendipcintegration.cpp
index 54a31a66622..cff37a1625c 100644
--- a/src/plugins/clangcodemodel/clangbackendipcintegration.cpp
+++ b/src/plugins/clangcodemodel/clangbackendipcintegration.cpp
@@ -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);
diff --git a/src/plugins/clangcodemodel/clangbackendipcintegration.h b/src/plugins/clangcodemodel/clangbackendipcintegration.h
index f73f8035d9f..6bbd692e911 100644
--- a/src/plugins/clangcodemodel/clangbackendipcintegration.h
+++ b/src/plugins/clangcodemodel/clangbackendipcintegration.h
@@ -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,
diff --git a/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp b/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp
index c0722a3907d..800d4baf0e2 100644
--- a/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp
+++ b/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp
@@ -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)
diff --git a/src/plugins/clangcodemodel/clangeditordocumentprocessor.h b/src/plugins/clangcodemodel/clangeditordocumentprocessor.h
index 4760e8bb36c..cdee5b7d861 100644
--- a/src/plugins/clangcodemodel/clangeditordocumentprocessor.h
+++ b/src/plugins/clangcodemodel/clangeditordocumentprocessor.h
@@ -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;
diff --git a/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp b/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp
index f06753ebc63..40296db5639 100644
--- a/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp
+++ b/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp
@@ -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)); }
diff --git a/src/plugins/cppeditor/cppuseselectionsupdater.cpp b/src/plugins/cppeditor/cppuseselectionsupdater.cpp
index bc6530fe058..217a58fa1d2 100644
--- a/src/plugins/cppeditor/cppuseselectionsupdater.cpp
+++ b/src/plugins/cppeditor/cppuseselectionsupdater.cpp
@@ -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());
}
diff --git a/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri b/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri
index cf0a0e6682e..c598eece8b5 100644
--- a/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri
+++ b/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri
@@ -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
diff --git a/src/tools/clangbackend/ipcsource/clangcodemodelserver.cpp b/src/tools/clangbackend/ipcsource/clangcodemodelserver.cpp
index d310aa523dc..1f6c3263a34 100644
--- a/src/tools/clangbackend/ipcsource/clangcodemodelserver.cpp
+++ b/src/tools/clangbackend/ipcsource/clangcodemodelserver.cpp
@@ -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");
diff --git a/src/tools/clangbackend/ipcsource/clangcodemodelserver.h b/src/tools/clangbackend/ipcsource/clangcodemodelserver.h
index 90e7191423e..70d99b5afa3 100644
--- a/src/tools/clangbackend/ipcsource/clangcodemodelserver.h
+++ b/src/tools/clangbackend/ipcsource/clangcodemodelserver.h
@@ -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;
diff --git a/src/tools/clangbackend/ipcsource/clangdocumentprocessor.cpp b/src/tools/clangbackend/ipcsource/clangdocumentprocessor.cpp
index fdefb9ab447..1aa59062146 100644
--- a/src/tools/clangbackend/ipcsource/clangdocumentprocessor.cpp
+++ b/src/tools/clangbackend/ipcsource/clangdocumentprocessor.cpp
@@ -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();
diff --git a/src/tools/clangbackend/ipcsource/clangdocumentprocessor.h b/src/tools/clangbackend/ipcsource/clangdocumentprocessor.h
index dc193f83628..9c3fb147f94 100644
--- a/src/tools/clangbackend/ipcsource/clangdocumentprocessor.h
+++ b/src/tools/clangbackend/ipcsource/clangdocumentprocessor.h
@@ -67,6 +67,7 @@ public:
public: // for tests
bool isSupportiveTranslationUnitInitialized() const;
+ JobRequests &queue();
QList<Jobs::RunningJob> runningJobs() const;
int queueSize() const;
diff --git a/src/tools/clangbackend/ipcsource/clangiasyncjob.cpp b/src/tools/clangbackend/ipcsource/clangiasyncjob.cpp
index d6fc0ff659f..14de731eb62 100644
--- a/src/tools/clangbackend/ipcsource/clangiasyncjob.cpp
+++ b/src/tools/clangbackend/ipcsource/clangiasyncjob.cpp
@@ -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;
diff --git a/src/tools/clangbackend/ipcsource/clangjobqueue.cpp b/src/tools/clangbackend/ipcsource/clangjobqueue.cpp
index a6041ef9739..e76558051af 100644
--- a/src/tools/clangbackend/ipcsource/clangjobqueue.cpp
+++ b/src/tools/clangbackend/ipcsource/clangjobqueue.cpp
@@ -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;
}
diff --git a/src/tools/clangbackend/ipcsource/clangjobqueue.h b/src/tools/clangbackend/ipcsource/clangjobqueue.h
index 82c46b5a1c2..736ff3f2a3b 100644
--- a/src/tools/clangbackend/ipcsource/clangjobqueue.h
+++ b/src/tools/clangbackend/ipcsource/clangjobqueue.h
@@ -52,7 +52,7 @@ public:
const IsJobRunningForJobRequestHandler &isJobRunningHandler);
public: // for tests
- JobRequests queue() const;
+ JobRequests &queue();
int size() const;
void prioritizeRequests();
diff --git a/src/tools/clangbackend/ipcsource/clangjobrequest.cpp b/src/tools/clangbackend/ipcsource/clangjobrequest.cpp
index 90fe57da256..bb9eb9bb993 100644
--- a/src/tools/clangbackend/ipcsource/clangjobrequest.cpp
+++ b/src/tools/clangbackend/ipcsource/clangjobrequest.cpp
@@ -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
diff --git a/src/tools/clangbackend/ipcsource/clangjobrequest.h b/src/tools/clangbackend/ipcsource/clangjobrequest.h
index 83d8c3947ac..a931514e854 100644
--- a/src/tools/clangbackend/ipcsource/clangjobrequest.h
+++ b/src/tools/clangbackend/ipcsource/clangjobrequest.h
@@ -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;
diff --git a/src/tools/clangbackend/ipcsource/clangjobs.cpp b/src/tools/clangbackend/ipcsource/clangjobs.cpp
index 424cc85f1db..dfe619546cb 100644
--- a/src/tools/clangbackend/ipcsource/clangjobs.cpp
+++ b/src/tools/clangbackend/ipcsource/clangjobs.cpp
@@ -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();
}
diff --git a/src/tools/clangbackend/ipcsource/clangjobs.h b/src/tools/clangbackend/ipcsource/clangjobs.h
index 794577da910..c4167110f5e 100644
--- a/src/tools/clangbackend/ipcsource/clangjobs.h
+++ b/src/tools/clangbackend/ipcsource/clangjobs.h
@@ -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;
diff --git a/src/tools/clangbackend/ipcsource/clangreferencescollector.cpp b/src/tools/clangbackend/ipcsource/clangreferencescollector.cpp
new file mode 100644
index 00000000000..695b30dc66a
--- /dev/null
+++ b/src/tools/clangbackend/ipcsource/clangreferencescollector.cpp
@@ -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
diff --git a/src/tools/clangbackend/ipcsource/clangreferencescollector.h b/src/tools/clangbackend/ipcsource/clangreferencescollector.h
new file mode 100644
index 00000000000..1fa16967e64
--- /dev/null
+++ b/src/tools/clangbackend/ipcsource/clangreferencescollector.h
@@ -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
+
diff --git a/src/tools/clangbackend/ipcsource/clangrequestreferencesjob.cpp b/src/tools/clangbackend/ipcsource/clangrequestreferencesjob.cpp
new file mode 100644
index 00000000000..f80072c0ed4
--- /dev/null
+++ b/src/tools/clangbackend/ipcsource/clangrequestreferencesjob.cpp
@@ -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
diff --git a/src/tools/clangbackend/ipcsource/clangrequestreferencesjob.h b/src/tools/clangbackend/ipcsource/clangrequestreferencesjob.h
new file mode 100644
index 00000000000..179f8b845b8
--- /dev/null
+++ b/src/tools/clangbackend/ipcsource/clangrequestreferencesjob.h
@@ -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
diff --git a/src/tools/clangbackend/ipcsource/clangtranslationunit.cpp b/src/tools/clangbackend/ipcsource/clangtranslationunit.cpp
index f4d3390038f..ca95c5f190b 100644
--- a/src/tools/clangbackend/ipcsource/clangtranslationunit.cpp
+++ b/src/tools/clangbackend/ipcsource/clangtranslationunit.cpp
@@ -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));
diff --git a/src/tools/clangbackend/ipcsource/clangtranslationunit.h b/src/tools/clangbackend/ipcsource/clangtranslationunit.h
index 69574750923..5e6d28736a9 100644
--- a/src/tools/clangbackend/ipcsource/clangtranslationunit.h
+++ b/src/tools/clangbackend/ipcsource/clangtranslationunit.h
@@ -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;
diff --git a/src/tools/clangbackend/ipcsource/clangtype.cpp b/src/tools/clangbackend/ipcsource/clangtype.cpp
index 13ab8c9c6c0..6b14b38dd84 100644
--- a/src/tools/clangbackend/ipcsource/clangtype.cpp
+++ b/src/tools/clangbackend/ipcsource/clangtype.cpp
@@ -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));
diff --git a/src/tools/clangbackend/ipcsource/clangtype.h b/src/tools/clangbackend/ipcsource/clangtype.h
index 6a51245ce6b..71ea1506ee5 100644
--- a/src/tools/clangbackend/ipcsource/clangtype.h
+++ b/src/tools/clangbackend/ipcsource/clangtype.h
@@ -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;
diff --git a/src/tools/clangbackend/ipcsource/cursor.cpp b/src/tools/clangbackend/ipcsource/cursor.cpp
index 1bc494ec71f..6367955fe4c 100644
--- a/src/tools/clangbackend/ipcsource/cursor.cpp
+++ b/src/tools/clangbackend/ipcsource/cursor.cpp
@@ -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);
diff --git a/src/tools/clangbackend/ipcsource/cursor.h b/src/tools/clangbackend/ipcsource/cursor.h
index 8b5e72df3d1..e1420f596da 100644
--- a/src/tools/clangbackend/ipcsource/cursor.h
+++ b/src/tools/clangbackend/ipcsource/cursor.h
@@ -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;
diff --git a/src/tools/clangbackend/ipcsource/sourcerange.cpp b/src/tools/clangbackend/ipcsource/sourcerange.cpp
index 62c52249d46..86029cfd3ee 100644
--- a/src/tools/clangbackend/ipcsource/sourcerange.cpp
+++ b/src/tools/clangbackend/ipcsource/sourcerange.cpp
@@ -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(),
diff --git a/src/tools/clangbackend/ipcsource/sourcerange.h b/src/tools/clangbackend/ipcsource/sourcerange.h
index df5612004e1..955896ccfb0 100644
--- a/src/tools/clangbackend/ipcsource/sourcerange.h
+++ b/src/tools/clangbackend/ipcsource/sourcerange.h
@@ -49,6 +49,8 @@ public:
SourceLocation start() const;
SourceLocation end() const;
+ bool contains(unsigned line, unsigned column) const;
+
SourceRangeContainer toSourceRangeContainer() const;
operator CXSourceRange() const;
diff --git a/tests/unit/echoserver/echoclangcodemodelserver.cpp b/tests/unit/echoserver/echoclangcodemodelserver.cpp
index d9495266269..b9c465e1a70 100644
--- a/tests/unit/echoserver/echoclangcodemodelserver.cpp
+++ b/tests/unit/echoserver/echoclangcodemodelserver.cpp
@@ -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);
diff --git a/tests/unit/echoserver/echoclangcodemodelserver.h b/tests/unit/echoserver/echoclangcodemodelserver.h
index c775dafc67f..d381c0bdea0 100644
--- a/tests/unit/echoserver/echoclangcodemodelserver.h
+++ b/tests/unit/echoserver/echoclangcodemodelserver.h
@@ -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:
diff --git a/tests/unit/unittest/clangipcserver-test.cpp b/tests/unit/unittest/clangipcserver-test.cpp
index 4171b47cb70..d90c0d30fb2 100644
--- a/tests/unit/unittest/clangipcserver-test.cpp
+++ b/tests/unit/unittest/clangipcserver-test.cpp
@@ -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;
diff --git a/tests/unit/unittest/clangjobqueue-test.cpp b/tests/unit/unittest/clangjobqueue-test.cpp
index a16e4c87198..22af7aa01a7 100644
--- a/tests/unit/unittest/clangjobqueue-test.cpp
+++ b/tests/unit/unittest/clangjobqueue-test.cpp
@@ -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)});
diff --git a/tests/unit/unittest/clangjobs-test.cpp b/tests/unit/unittest/clangjobs-test.cpp
index 427dab2e06f..cf3507d1087 100644
--- a/tests/unit/unittest/clangjobs-test.cpp
+++ b/tests/unit/unittest/clangjobs-test.cpp
@@ -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();
diff --git a/tests/unit/unittest/clangreferencescollector-test.cpp b/tests/unit/unittest/clangreferencescollector-test.cpp
new file mode 100644
index 00000000000..4373450e3d5
--- /dev/null
+++ b/tests/unit/unittest/clangreferencescollector-test.cpp
@@ -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
diff --git a/tests/unit/unittest/clangrequestreferencesjob-test.cpp b/tests/unit/unittest/clangrequestreferencesjob-test.cpp
new file mode 100644
index 00000000000..234d8258836
--- /dev/null
+++ b/tests/unit/unittest/clangrequestreferencesjob-test.cpp
@@ -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
diff --git a/tests/unit/unittest/clientserverinprocess-test.cpp b/tests/unit/unittest/clientserverinprocess-test.cpp
index d4a1cab16dd..92c3597c5ca 100644
--- a/tests/unit/unittest/clientserverinprocess-test.cpp
+++ b/tests/unit/unittest/clientserverinprocess-test.cpp
@@ -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>
diff --git a/tests/unit/unittest/data/references.cpp b/tests/unit/unittest/data/references.cpp
new file mode 100644
index 00000000000..d71a9e8f798
--- /dev/null
+++ b/tests/unit/unittest/data/references.cpp
@@ -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);
+}
diff --git a/tests/unit/unittest/dummyclangipcclient.h b/tests/unit/unittest/dummyclangipcclient.h
index c12bdcd832b..a9dca6ead60 100644
--- a/tests/unit/unittest/dummyclangipcclient.h
+++ b/tests/unit/unittest/dummyclangipcclient.h
@@ -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 {}
};
diff --git a/tests/unit/unittest/mockclangcodemodelclient.h b/tests/unit/unittest/mockclangcodemodelclient.h
index 650980a9ab3..a919de423e1 100644
--- a/tests/unit/unittest/mockclangcodemodelclient.h
+++ b/tests/unit/unittest/mockclangcodemodelclient.h
@@ -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));
};
diff --git a/tests/unit/unittest/mockclangcodemodelserver.h b/tests/unit/unittest/mockclangcodemodelserver.h
index c2ef3cfd09a..6285c48d2c2 100644
--- a/tests/unit/unittest/mockclangcodemodelserver.h
+++ b/tests/unit/unittest/mockclangcodemodelserver.h
@@ -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));
};
diff --git a/tests/unit/unittest/readandwritemessageblock-test.cpp b/tests/unit/unittest/readandwritemessageblock-test.cpp
index ace9effadb4..4d6e663a874 100644
--- a/tests/unit/unittest/readandwritemessageblock-test.cpp
+++ b/tests/unit/unittest/readandwritemessageblock-test.cpp
@@ -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();
diff --git a/tests/unit/unittest/unittest.pro b/tests/unit/unittest/unittest.pro
index 382df1df904..b5402ff3e0d 100644
--- a/tests/unit/unittest/unittest.pro
+++ b/tests/unit/unittest/unittest.pro
@@ -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 \