diff options
author | Nikolai Kosjar <nikolai.kosjar@qt.io> | 2017-06-09 12:19:09 +0200 |
---|---|---|
committer | Nikolai Kosjar <nikolai.kosjar@qt.io> | 2017-06-14 12:49:05 +0000 |
commit | ca72c29462d9caae5e6095f8361f843758c5972c (patch) | |
tree | 21a39edf9b3fb1ab5090a83076e7483dc425d6fa | |
parent | 9de9da742333d6114b337abf4b64b15aa23afa9f (diff) |
Clang: Provide highlighting for identifier under cursor
Change-Id: I80ffe23cbcc84ab7323124581d9dd6afbe974fd0
Reviewed-by: Marco Bubke <marco.bubke@qt.io>
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 ¶ms) { - 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 ¤tEditor, 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 \ |