From 0b6c2422b879365cf0cf729f60fb8f2fc5c2c803 Mon Sep 17 00:00:00 2001 From: Orgad Shaneh Date: Fri, 12 Aug 2016 16:36:12 +0300 Subject: [PATCH 01/27] Project: Disable deprecated functions Change-Id: Ifd26ab4237664c0887b521e867cf801a65d49fcd Reviewed-by: Christian Kandeler Reviewed-by: Eike Ziller --- qbs/modules/qtc/qtc.qbs | 3 ++- qtcreator.pri | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/qbs/modules/qtc/qtc.qbs b/qbs/modules/qtc/qtc.qbs index b86dbb3edbe..69d2f6c8762 100644 --- a/qbs/modules/qtc/qtc.qbs +++ b/qbs/modules/qtc/qtc.qbs @@ -63,7 +63,8 @@ Module { "QT_CREATOR", 'IDE_LIBRARY_BASENAME="' + libDirName + '"', "QT_NO_CAST_TO_ASCII", - "QT_RESTRICTED_CAST_FROM_ASCII" + "QT_RESTRICTED_CAST_FROM_ASCII", + "QT_DISABLE_DEPRECATED_BEFORE=0x050600", ].concat(testsEnabled ? ["WITH_TESTS"] : []) Rule { diff --git a/qtcreator.pri b/qtcreator.pri index 8997b4a9961..1fcae0baf25 100644 --- a/qtcreator.pri +++ b/qtcreator.pri @@ -179,7 +179,11 @@ exists($$IDE_LIBRARY_PATH): LIBS *= -L$$IDE_LIBRARY_PATH # library path from ou DEFINES += IDE_LIBRARY_BASENAME=\\\"$$IDE_LIBRARY_BASENAME\\\" } -DEFINES += QT_CREATOR QT_NO_CAST_TO_ASCII QT_RESTRICTED_CAST_FROM_ASCII +DEFINES += \ + QT_CREATOR \ + QT_NO_CAST_TO_ASCII \ + QT_RESTRICTED_CAST_FROM_ASCII \ + QT_DISABLE_DEPRECATED_BEFORE=0x050600 !macx:DEFINES += QT_USE_FAST_OPERATOR_PLUS QT_USE_FAST_CONCATENATION unix { From 318b6c9dbad40ddea230636108aa52fbd3d9034a Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Sun, 9 Oct 2016 11:55:09 +0200 Subject: [PATCH 02/27] JSON Wizards: Add "isPassword" property to LineEdits Change-Id: I5539c66c162345bda052546fa02cc69d4bd55f9a Reviewed-by: Leena Miettinen --- doc/src/projects/creator-projects-custom-wizards-json.qdoc | 3 +++ src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp | 3 +++ src/plugins/projectexplorer/jsonwizard/jsonfieldpage_p.h | 1 + 3 files changed, 7 insertions(+) diff --git a/doc/src/projects/creator-projects-custom-wizards-json.qdoc b/doc/src/projects/creator-projects-custom-wizards-json.qdoc index 04c6631ffac..ea75906386c 100644 --- a/doc/src/projects/creator-projects-custom-wizards-json.qdoc +++ b/doc/src/projects/creator-projects-custom-wizards-json.qdoc @@ -824,6 +824,9 @@ For example, to turn the first character in the line edit to upper case. + \li \c isPassword is a boolean value that specifies that the line edit + contains a password, which will be masked. + \endlist \section2 Path Chooser diff --git a/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp b/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp index fcabaaeed25..aeb799ae027 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp +++ b/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp @@ -404,6 +404,7 @@ bool LineEditField::parseData(const QVariant &data, QString *errorMessage) QVariantMap tmp = data.toMap(); + m_isPassword = tmp.value("isPassword", false).toBool(); m_defaultText = JsonWizardFactory::localizedString(tmp.value(QLatin1String("trText")).toString()); m_disabledText = JsonWizardFactory::localizedString(tmp.value(QLatin1String("trDisabledText")).toString()); m_placeholderText = JsonWizardFactory::localizedString(tmp.value(QLatin1String("trPlaceholder")).toString()); @@ -439,6 +440,8 @@ QWidget *LineEditField::createWidget(const QString &displayName, JsonFieldPage * if (!m_historyId.isEmpty()) w->setHistoryCompleter(m_historyId, m_restoreLastHistoryItem); + w->setEchoMode(m_isPassword ? QLineEdit::Password : QLineEdit::Normal); + return w; } diff --git a/src/plugins/projectexplorer/jsonwizard/jsonfieldpage_p.h b/src/plugins/projectexplorer/jsonwizard/jsonfieldpage_p.h index 657fa518d5b..e02723a5ec6 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonfieldpage_p.h +++ b/src/plugins/projectexplorer/jsonwizard/jsonfieldpage_p.h @@ -105,6 +105,7 @@ private: bool m_isModified; bool m_isValidating; bool m_restoreLastHistoryItem; + bool m_isPassword; QString m_placeholderText; QString m_defaultText; QString m_disabledText; From 82803f5a68b12b3b1b8d05f6657b83c0d7a3565d Mon Sep 17 00:00:00 2001 From: hjk Date: Fri, 7 Oct 2016 16:46:43 +0200 Subject: [PATCH 03/27] Valgrind: Create only one extra configuration aspect Let only the Callgrind tool create one, Memcheck shares it now. Task-number: QTCREATORBUG-17053 Change-Id: I29d17ef5801ab295c2523f0748616b9e2aab29ce Reviewed-by: Nikolai Kosjar --- src/plugins/valgrind/memchecktool.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/plugins/valgrind/memchecktool.cpp b/src/plugins/valgrind/memchecktool.cpp index 89e7c40f6aa..ca51ee56dff 100644 --- a/src/plugins/valgrind/memchecktool.cpp +++ b/src/plugins/valgrind/memchecktool.cpp @@ -711,10 +711,11 @@ public: return m_tool->createRunControl(runConfiguration, mode); } - IRunConfigurationAspect *createRunConfigurationAspect(ProjectExplorer::RunConfiguration *rc) override - { - return createValgrindRunConfigurationAspect(rc); - } + // Do not create an aspect, let the Callgrind tool create one and use that, too. +// IRunConfigurationAspect *createRunConfigurationAspect(ProjectExplorer::RunConfiguration *rc) override +// { +// return createValgrindRunConfigurationAspect(rc); +// } public: MemcheckTool *m_tool; From b64bb0a7e3f4529282f4a27fa9fa72bc648673cf Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Tue, 6 Sep 2016 11:38:19 +0200 Subject: [PATCH 04/27] Help: Fix that help viewer opened even if URL is opened in browser After we constructed the online URL for Qt/Qt Creator documentation that is not installed locally, we can just open that via desktop services instead of bothering built-in help viewers. Change-Id: Ic8a37bc22d34af881b5daf87534d59db4d331e44 Task-number: QTCREATORBUG-16111 Reviewed-by: Robert Loehning --- src/plugins/help/helpplugin.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/plugins/help/helpplugin.cpp b/src/plugins/help/helpplugin.cpp index f50a8ee5b84..115bdfbca4c 100644 --- a/src/plugins/help/helpplugin.cpp +++ b/src/plugins/help/helpplugin.cpp @@ -622,25 +622,25 @@ void HelpPlugin::handleHelpRequest(const QUrl &url, HelpManager::HelpViewerLocat if (HelpViewer::launchWithExternalApp(url)) return; - QString address = url.toString(); if (!HelpManager::findFile(url).isValid()) { + const QString address = url.toString(); if (address.startsWith("qthelp://org.qt-project.") - || address.startsWith("qthelp://com.nokia.") - || address.startsWith("qthelp://com.trolltech.")) { - // local help not installed, resort to external web help - QString urlPrefix = "http://doc.qt.io/"; - if (url.authority() == "org.qt-project.qtcreator") - urlPrefix.append(QString::fromLatin1("qtcreator")); - else - urlPrefix.append("qt-5"); - address = urlPrefix + address.mid(address.lastIndexOf(QLatin1Char('/'))); + || address.startsWith("qthelp://com.nokia.") + || address.startsWith("qthelp://com.trolltech.")) { + // local help not installed, resort to external web help + QString urlPrefix = "http://doc.qt.io/"; + if (url.authority() == "org.qt-project.qtcreator") + urlPrefix.append(QString::fromLatin1("qtcreator")); + else + urlPrefix.append("qt-5"); + QDesktopServices::openUrl(QUrl(urlPrefix + address.mid(address.lastIndexOf(QLatin1Char('/'))))); + return; } } - const QUrl newUrl(address); HelpViewer *viewer = viewerForHelpViewerLocation(location); QTC_ASSERT(viewer, return); - viewer->setSource(newUrl); + viewer->setSource(url); ICore::raiseWindow(viewer); } From c12d01fb4176535736c1a3e0f483be6361326700 Mon Sep 17 00:00:00 2001 From: Nikolai Kosjar Date: Thu, 8 Sep 2016 15:49:54 +0200 Subject: [PATCH 05/27] Clang: Add job queue for each document ...in preparation for follow-up changes. This will enable e.g. a timer per document. This does not change any behavior yet. Change-Id: Ic1dc06de602373c666d47ce7a95ab99e56d389d5 Reviewed-by: David Schulz --- .../clangbackend/ipcsource/clangasyncjob.h | 5 + .../ipcsource/clangbackendclangipc-source.pri | 8 +- .../ipcsource/clangcodemodelserver.cpp | 63 +++--- .../ipcsource/clangcodemodelserver.h | 13 +- .../ipcsource/clangdocumentprocessor.cpp | 89 ++++++++ .../ipcsource/clangdocumentprocessor.h | 65 ++++++ .../ipcsource/clangdocumentprocessors.cpp | 113 ++++++++++ .../ipcsource/clangdocumentprocessors.h | 74 +++++++ .../ipcsource/clangexceptions.cpp | 20 ++ .../clangbackend/ipcsource/clangexceptions.h | 14 ++ .../clangbackend/ipcsource/clangiasyncjob.h | 2 + .../clangbackend/ipcsource/clangjobs.cpp | 10 +- src/tools/clangbackend/ipcsource/clangjobs.h | 2 +- .../unittest/clangdocumentprocessor-test.cpp | 128 ++++++++++++ .../unittest/clangdocumentprocessors-test.cpp | 194 ++++++++++++++++++ tests/unit/unittest/clangipcserver-test.cpp | 4 +- tests/unit/unittest/clangjobs-test.cpp | 8 +- tests/unit/unittest/unittest.pro | 2 + 18 files changed, 771 insertions(+), 43 deletions(-) create mode 100644 src/tools/clangbackend/ipcsource/clangdocumentprocessor.cpp create mode 100644 src/tools/clangbackend/ipcsource/clangdocumentprocessor.h create mode 100644 src/tools/clangbackend/ipcsource/clangdocumentprocessors.cpp create mode 100644 src/tools/clangbackend/ipcsource/clangdocumentprocessors.h create mode 100644 tests/unit/unittest/clangdocumentprocessor-test.cpp create mode 100644 tests/unit/unittest/clangdocumentprocessors-test.cpp diff --git a/src/tools/clangbackend/ipcsource/clangasyncjob.h b/src/tools/clangbackend/ipcsource/clangasyncjob.h index b44227041e5..c8a7b799473 100644 --- a/src/tools/clangbackend/ipcsource/clangasyncjob.h +++ b/src/tools/clangbackend/ipcsource/clangasyncjob.h @@ -64,6 +64,11 @@ public: return future; } + void preventFinalization() override + { + m_futureWatcher.disconnect(); + } + private: Runner m_runner; QFutureWatcher m_futureWatcher; diff --git a/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri b/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri index b247735deda..dc468470f56 100644 --- a/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri +++ b/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri @@ -41,7 +41,9 @@ HEADERS += $$PWD/clangcodemodelserver.h \ $$PWD/clangtranslationunit.h \ $$PWD/clangunsavedfilesshallowarguments.h \ $$PWD/clangupdatedocumentannotationsjob.h \ - $$PWD/clangexceptions.h + $$PWD/clangexceptions.h \ + $$PWD/clangdocumentprocessor.h \ + $$PWD/clangdocumentprocessors.h \ SOURCES += $$PWD/clangcodemodelserver.cpp \ $$PWD/codecompleter.cpp \ @@ -80,4 +82,6 @@ SOURCES += $$PWD/clangcodemodelserver.cpp \ $$PWD/clangtranslationunit.cpp \ $$PWD/clangunsavedfilesshallowarguments.cpp \ $$PWD/clangupdatedocumentannotationsjob.cpp \ - $$PWD/clangexceptions.cpp + $$PWD/clangexceptions.cpp \ + $$PWD/clangdocumentprocessor.cpp \ + $$PWD/clangdocumentprocessors.cpp \ diff --git a/src/tools/clangbackend/ipcsource/clangcodemodelserver.cpp b/src/tools/clangbackend/ipcsource/clangcodemodelserver.cpp index 3c1cee59777..1b26c39fadf 100644 --- a/src/tools/clangbackend/ipcsource/clangcodemodelserver.cpp +++ b/src/tools/clangbackend/ipcsource/clangcodemodelserver.cpp @@ -141,6 +141,10 @@ void ClangCodeModelServer::unregisterTranslationUnitsForEditor(const ClangBackEn TIME_SCOPE_DURATION("ClangCodeModelServer::unregisterTranslationUnitsForEditor"); try { + for (const auto &fileContainer : message.fileContainers()) { + const Document &document = documents.document(fileContainer); + documentProcessors().remove(document); + } documents.remove(message.fileContainers()); unsavedFiles.remove(message.fileContainers()); } catch (const std::exception &exception) { @@ -211,8 +215,9 @@ void ClangCodeModelServer::completeCode(const ClangBackEnd::CompleteCodeMessage jobRequest.column = message.column(); jobRequest.ticketNumber = message.ticketNumber(); - jobs().add(jobRequest); - jobs().process(); + DocumentProcessor processor = documentProcessors().processor(document); + processor.addJob(jobRequest); + processor.process(); } catch (const std::exception &exception) { qWarning() << "Error in ClangCodeModelServer::completeCode:" << exception.what(); } @@ -229,8 +234,9 @@ void ClangCodeModelServer::requestDocumentAnnotations(const RequestDocumentAnnot const JobRequest jobRequest = createJobRequest(document, JobRequest::Type::RequestDocumentAnnotations); - jobs().add(jobRequest); - jobs().process(); + DocumentProcessor processor = documentProcessors().processor(document); + processor.addJob(jobRequest); + processor.process(); } catch (const std::exception &exception) { qWarning() << "Error in ClangCodeModelServer::requestDocumentAnnotations:" << exception.what(); } @@ -260,9 +266,14 @@ void ClangCodeModelServer::startDocumentAnnotationsTimerIfFileIsNotOpenAsDocumen updateDocumentAnnotationsTimer.start(0); } -const Jobs &ClangCodeModelServer::jobsForTestOnly() +QList ClangCodeModelServer::runningJobsForTestsOnly() { - return jobs(); + return documentProcessors().runningJobs(); +} + +int ClangCodeModelServer::queueSizeForTestsOnly() +{ + return documentProcessors().queueSize(); } bool ClangCodeModelServer::isTimerRunningForTestOnly() const @@ -270,28 +281,26 @@ bool ClangCodeModelServer::isTimerRunningForTestOnly() const return updateDocumentAnnotationsTimer.isActive(); } -void ClangCodeModelServer::addJobRequestsForDirtyAndVisibleDocuments() -{ - for (const auto &document : documents.documents()) { - if (document.isNeedingReparse() && document.isVisibleInEditor()) - jobs().add(createJobRequest(document, JobRequest::Type::UpdateDocumentAnnotations)); - } -} - void ClangCodeModelServer::processJobsForDirtyAndVisibleDocuments() { - addJobRequestsForDirtyAndVisibleDocuments(); - jobs().process(); + for (const auto &document : documents.documents()) { + if (document.isNeedingReparse() && document.isVisibleInEditor()) { + DocumentProcessor processor = documentProcessors().processor(document); + processor.addJob(createJobRequest(document, JobRequest::Type::UpdateDocumentAnnotations)); + } + } + + documentProcessors().process(); } void ClangCodeModelServer::processInitialJobsForDocuments(const std::vector &documents) { for (const auto &document : documents) { - jobs().add(createJobRequest(document, JobRequest::Type::UpdateDocumentAnnotations)); - jobs().add(createJobRequest(document, JobRequest::Type::CreateInitialDocumentPreamble)); + DocumentProcessor processor = documentProcessors().create(document); + processor.addJob(createJobRequest(document, JobRequest::Type::UpdateDocumentAnnotations)); + processor.addJob(createJobRequest(document, JobRequest::Type::CreateInitialDocumentPreamble)); + processor.process(); } - - jobs().process(); } JobRequest ClangCodeModelServer::createJobRequest(const Document &document, @@ -315,16 +324,16 @@ void ClangCodeModelServer::setUpdateDocumentAnnotationsTimeOutInMsForTestsOnly(i updateDocumentAnnotationsTimeOutInMs = value; } -Jobs &ClangCodeModelServer::jobs() +DocumentProcessors &ClangCodeModelServer::documentProcessors() { - if (!jobs_) { - // Jobs needs a reference to the client, but the client is not known at - // construction time of ClangCodeModelServer, so construct Jobs in a - // lazy manner. - jobs_.reset(new Jobs(documents, unsavedFiles, projects, *client())); + if (!documentProcessors_) { + // DocumentProcessors needs a reference to the client, but the client + // is not known at construction time of ClangCodeModelServer, so + // construct DocumentProcessors in a lazy manner. + documentProcessors_.reset(new DocumentProcessors(documents, unsavedFiles, projects, *client())); } - return *jobs_.data(); + return *documentProcessors_.data(); } } // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangcodemodelserver.h b/src/tools/clangbackend/ipcsource/clangcodemodelserver.h index ff0db141f9c..efebb054da9 100644 --- a/src/tools/clangbackend/ipcsource/clangcodemodelserver.h +++ b/src/tools/clangbackend/ipcsource/clangcodemodelserver.h @@ -31,8 +31,9 @@ #include "projects.h" #include "clangdocument.h" #include "clangdocuments.h" +#include "clangdocumentprocessors.h" +#include "clangjobrequest.h" #include "unsavedfiles.h" -#include "clangjobs.h" #include @@ -58,14 +59,15 @@ public: void updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message) override; void requestDocumentAnnotations(const RequestDocumentAnnotationsMessage &message) override; -public /*for tests*/: +public: // for tests const Documents &documentsForTestOnly() const; - const Jobs &jobsForTestOnly(); + QList runningJobsForTestsOnly(); + int queueSizeForTestsOnly(); bool isTimerRunningForTestOnly() const; void setUpdateDocumentAnnotationsTimeOutInMsForTestsOnly(int value); private: - Jobs &jobs(); + DocumentProcessors &documentProcessors(); void startDocumentAnnotationsTimerIfFileIsNotOpenAsDocument(const Utf8String &filePath); void addJobRequestsForDirtyAndVisibleDocuments(); @@ -78,7 +80,8 @@ private: ProjectParts projects; UnsavedFiles unsavedFiles; Documents documents; - QScopedPointer jobs_; + + QScopedPointer documentProcessors_; // Delayed initialization QTimer updateDocumentAnnotationsTimer; int updateDocumentAnnotationsTimeOutInMs; diff --git a/src/tools/clangbackend/ipcsource/clangdocumentprocessor.cpp b/src/tools/clangbackend/ipcsource/clangdocumentprocessor.cpp new file mode 100644 index 00000000000..0b4faf9251f --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangdocumentprocessor.cpp @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "clangdocumentprocessor.h" + +#include "clangjobs.h" + +#include "clangdocument.h" + +namespace ClangBackEnd { + +class DocumentProcessorData +{ +public: + DocumentProcessorData(const Document &document, + Documents &documents, + UnsavedFiles &unsavedFiles, + ProjectParts &projects, + ClangCodeModelClientInterface &client) + : document(document) + , jobs(documents, unsavedFiles, projects, client) + {} + +public: + Document document; + Jobs jobs; +}; + +DocumentProcessor::DocumentProcessor(const Document &document, + Documents &documents, + UnsavedFiles &unsavedFiles, + ProjectParts &projects, + ClangCodeModelClientInterface &client) + : d(std::make_shared(document, + documents, + unsavedFiles, + projects, + client)) +{ +} + +void DocumentProcessor::addJob(const JobRequest &jobRequest) +{ + d->jobs.add(jobRequest); +} + +JobRequests DocumentProcessor::process() +{ + return d->jobs.process(); +} + +Document DocumentProcessor::document() const +{ + return d->document; +} + +QList DocumentProcessor::runningJobs() const +{ + return d->jobs.runningJobs(); +} + +int DocumentProcessor::queueSize() const +{ + return d->jobs.queue().size(); +} + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangdocumentprocessor.h b/src/tools/clangbackend/ipcsource/clangdocumentprocessor.h new file mode 100644 index 00000000000..0ad452b314a --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangdocumentprocessor.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "clangjobrequest.h" +#include "clangjobs.h" + +#include + +namespace ClangBackEnd { + +class ClangCodeModelClientInterface; +class Document; +class Documents; +class DocumentProcessorData; +class JobRequest; +class ProjectParts; +class UnsavedFiles; + +class DocumentProcessor +{ +public: + DocumentProcessor(const Document &document, + Documents &documents, + UnsavedFiles &unsavedFiles, + ProjectParts &projects, + ClangCodeModelClientInterface &client); + + void addJob(const JobRequest &jobRequest); + JobRequests process(); + + Document document() const; + +public: // for tests + QList runningJobs() const; + int queueSize() const; + +private: + std::shared_ptr d; +}; + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangdocumentprocessors.cpp b/src/tools/clangbackend/ipcsource/clangdocumentprocessors.cpp new file mode 100644 index 00000000000..3e465f8173d --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangdocumentprocessors.cpp @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "clangdocumentprocessors.h" +#include "clangdocument.h" +#include "clangexceptions.h" + +namespace ClangBackEnd { + +DocumentProcessors::DocumentProcessors(Documents &documents, + UnsavedFiles &unsavedFiles, + ProjectParts &projects, + ClangCodeModelClientInterface &client) + : m_documents(documents) + , m_unsavedFiles(unsavedFiles) + , m_projects(projects) + , m_client(client) +{ +} + +static bool operator<(const DocumentId &lhs, const DocumentId &rhs) +{ + return lhs.filePath < rhs.filePath + || (lhs.filePath == rhs.filePath && lhs.projectPartId < lhs.projectPartId); +} + +DocumentProcessor DocumentProcessors::create(const Document &document) +{ + const DocumentId id{document.filePath(), document.projectPartId()}; + if (m_processors.contains(id)) + throw DocumentProcessorAlreadyExists(document.filePath(), document.projectPartId()); + + const DocumentProcessor element(document, m_documents, m_unsavedFiles, m_projects, m_client); + m_processors.insert(id, element); + + return element; +} + +DocumentProcessor DocumentProcessors::processor(const Document &document) +{ + const DocumentId id{document.filePath(), document.projectPartId()}; + + const auto it = m_processors.find(id); + if (it == m_processors.end()) + throw DocumentProcessorDoesNotExist(document.filePath(), document.projectPartId()); + + return *it; +} + +QList DocumentProcessors::processors() const +{ + return m_processors.values(); +} + +void DocumentProcessors::remove(const Document &document) +{ + const DocumentId id{document.filePath(), document.projectPartId()}; + + const int itemsRemoved = m_processors.remove(id); + if (itemsRemoved != 1) + throw DocumentProcessorDoesNotExist(document.filePath(), document.projectPartId()); +} + +JobRequests DocumentProcessors::process() +{ + JobRequests jobsStarted; + for (auto &processor : m_processors) + jobsStarted += processor.process(); + + return jobsStarted; +} + +QList DocumentProcessors::runningJobs() const +{ + QList jobs; + for (auto &processor : m_processors) + jobs += processor.runningJobs(); + + return jobs; +} + +int DocumentProcessors::queueSize() const +{ + int total = 0; + for (auto &processor : m_processors) + total += processor.queueSize(); + + return total; +} + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangdocumentprocessors.h b/src/tools/clangbackend/ipcsource/clangdocumentprocessors.h new file mode 100644 index 00000000000..83e37f52766 --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangdocumentprocessors.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "clangdocumentprocessor.h" +#include "clangjobs.h" + +#include + +#include + +namespace ClangBackEnd { + +class Document; +class DocumentProcessor; + +class DocumentId { +public: + Utf8String filePath; + Utf8String projectPartId; +}; + +class DocumentProcessors +{ +public: + DocumentProcessors(Documents &documents, + UnsavedFiles &unsavedFiles, + ProjectParts &projects, + ClangCodeModelClientInterface &client); + + DocumentProcessor create(const Document &document); + DocumentProcessor processor(const Document &document); + void remove(const Document &document); + + JobRequests process(); + +public: // for tests + QList processors() const; + QList runningJobs() const; + int queueSize() const; + +private: + Documents &m_documents; + UnsavedFiles &m_unsavedFiles; + ProjectParts &m_projects; + ClangCodeModelClientInterface &m_client; + + QMap m_processors; +}; + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangexceptions.cpp b/src/tools/clangbackend/ipcsource/clangexceptions.cpp index e6d9218a943..4a85df4129a 100644 --- a/src/tools/clangbackend/ipcsource/clangexceptions.cpp +++ b/src/tools/clangbackend/ipcsource/clangexceptions.cpp @@ -74,4 +74,24 @@ DocumentIsNullException::DocumentIsNullException() m_info = Utf8String::fromUtf8("Tried to access a null Document!"); } +DocumentProcessorAlreadyExists::DocumentProcessorAlreadyExists(const Utf8String &filePath, + const Utf8String &projectPartId) +{ + m_info = Utf8StringLiteral("Document processor for file '") + + filePath + + Utf8StringLiteral("' and project part id '") + + projectPartId + + Utf8StringLiteral("' already exists!"); +} + +DocumentProcessorDoesNotExist::DocumentProcessorDoesNotExist(const Utf8String &filePath, + const Utf8String &projectPartId) +{ + m_info = Utf8StringLiteral("Document processor for file '") + + filePath + + Utf8StringLiteral("' and project part id '") + + projectPartId + + Utf8StringLiteral("' does not exist!"); +} + } // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangexceptions.h b/src/tools/clangbackend/ipcsource/clangexceptions.h index 6b1938cdfd8..1fbedccb38b 100644 --- a/src/tools/clangbackend/ipcsource/clangexceptions.h +++ b/src/tools/clangbackend/ipcsource/clangexceptions.h @@ -74,4 +74,18 @@ public: DocumentIsNullException(); }; +class DocumentProcessorAlreadyExists : public ClangBaseException +{ +public: + DocumentProcessorAlreadyExists(const Utf8String &filePath, + const Utf8String &projectPartId); +}; + +class DocumentProcessorDoesNotExist : public ClangBaseException +{ +public: + DocumentProcessorDoesNotExist(const Utf8String &filePath, + const Utf8String &projectPartId); +}; + } // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangiasyncjob.h b/src/tools/clangbackend/ipcsource/clangiasyncjob.h index 44f503f3cb8..dcc7df1c16f 100644 --- a/src/tools/clangbackend/ipcsource/clangiasyncjob.h +++ b/src/tools/clangbackend/ipcsource/clangiasyncjob.h @@ -56,6 +56,8 @@ public: virtual QFuture runAsync() = 0; virtual void finalizeAsyncRun() = 0; + virtual void preventFinalization() = 0; + public: // for tests bool isFinished() const; void setIsFinished(bool isFinished); diff --git a/src/tools/clangbackend/ipcsource/clangjobs.cpp b/src/tools/clangbackend/ipcsource/clangjobs.cpp index ac787c36fa5..c5202c15858 100644 --- a/src/tools/clangbackend/ipcsource/clangjobs.cpp +++ b/src/tools/clangbackend/ipcsource/clangjobs.cpp @@ -53,9 +53,15 @@ Jobs::Jobs(Documents &documents, Jobs::~Jobs() { + foreach (IAsyncJob *asyncJob, m_running.keys()) + asyncJob->preventFinalization(); + QFutureSynchronizer waitForFinishedJobs; foreach (const RunningJob &runningJob, m_running.values()) waitForFinishedJobs.addFuture(runningJob.future); + + foreach (IAsyncJob *asyncJob, m_running.keys()) + delete asyncJob; } void Jobs::add(const JobRequest &job) @@ -118,9 +124,9 @@ void Jobs::onJobFinished(IAsyncJob *asyncJob) process(); } -int Jobs::runningJobs() const +QList Jobs::runningJobs() const { - return m_running.size(); + return m_running.values(); } JobRequests Jobs::queue() const diff --git a/src/tools/clangbackend/ipcsource/clangjobs.h b/src/tools/clangbackend/ipcsource/clangjobs.h index 70b6dd80f6f..85d47488580 100644 --- a/src/tools/clangbackend/ipcsource/clangjobs.h +++ b/src/tools/clangbackend/ipcsource/clangjobs.h @@ -59,7 +59,7 @@ public: JobRequests process(); public /*for tests*/: - int runningJobs() const; + QList runningJobs() const; JobRequests queue() const; bool isJobRunning(const Utf8String &filePath, const Utf8String &projectPartId) const; diff --git a/tests/unit/unittest/clangdocumentprocessor-test.cpp b/tests/unit/unittest/clangdocumentprocessor-test.cpp new file mode 100644 index 00000000000..1674b224781 --- /dev/null +++ b/tests/unit/unittest/clangdocumentprocessor-test.cpp @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "clangiasyncjob.h" +#include "dummyclangipcclient.h" +#include "processevents-utilities.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "gtest-qt-printing.h" + +using namespace ClangBackEnd; + +namespace { + +class DocumentProcessor : public ::testing::Test +{ +protected: + void SetUp() override; + void TearDown() override; + + ClangBackEnd::JobRequest createJobRequest(ClangBackEnd::JobRequest::Type type) const; + + bool waitUntilAllJobsFinished(int timeOutInMs = 10000) const; + +protected: + ClangBackEnd::ProjectParts projects; + ClangBackEnd::UnsavedFiles unsavedFiles; + ClangBackEnd::Documents documents{projects, unsavedFiles}; + ClangBackEnd::Document document; + + DummyIpcClient dummyIpcClient; + + Utf8String filePath{Utf8StringLiteral(TESTDATA_DIR"/translationunits.cpp")}; + Utf8String projectPartId{Utf8StringLiteral("/path/to/projectfile")}; + + ClangBackEnd::DocumentProcessor documentProcessor{document, + documents, + unsavedFiles, + projects, + dummyIpcClient}; +}; + +TEST_F(DocumentProcessor, ProcessEmpty) +{ + const JobRequests jobsStarted = documentProcessor.process(); + + ASSERT_THAT(jobsStarted.size(), 0); +} + +TEST_F(DocumentProcessor, ProcessSingleJob) +{ + const JobRequest jobRequest = createJobRequest(JobRequest::Type::UpdateDocumentAnnotations); + documentProcessor.addJob(jobRequest); + + const JobRequests jobsStarted = documentProcessor.process(); + + ASSERT_THAT(jobsStarted.size(), 1); +} + +void DocumentProcessor::SetUp() +{ + projects.createOrUpdate({ProjectPartContainer(projectPartId)}); + + const QVector fileContainer{FileContainer(filePath, projectPartId)}; + document = documents.create(fileContainer).front(); + documents.setVisibleInEditors({filePath}); + documents.setUsedByCurrentEditor(filePath); +} + +void DocumentProcessor::TearDown() +{ + ASSERT_TRUE(waitUntilAllJobsFinished()); // QFuture/QFutureWatcher is implemented with events +} + +JobRequest DocumentProcessor::createJobRequest(JobRequest::Type type) const +{ + JobRequest jobRequest; + jobRequest.type = type; + jobRequest.requirements = JobRequest::requirementsForType(type); + jobRequest.filePath = filePath; + jobRequest.projectPartId = projectPartId; + jobRequest.unsavedFilesChangeTimePoint = unsavedFiles.lastChangeTimePoint(); + jobRequest.documentRevision = document.documentRevision(); + jobRequest.projectChangeTimePoint = projects.project(projectPartId).lastChangeTimePoint(); + + return jobRequest; +} + +bool DocumentProcessor::waitUntilAllJobsFinished(int timeOutInMs) const +{ + const auto noJobsRunningAnymore = [this](){ return documentProcessor.runningJobs().isEmpty(); }; + + return ProcessEventUtilities::processEventsUntilTrue(noJobsRunningAnymore, timeOutInMs); +} + +} // anonymous diff --git a/tests/unit/unittest/clangdocumentprocessors-test.cpp b/tests/unit/unittest/clangdocumentprocessors-test.cpp new file mode 100644 index 00000000000..7c36146aaba --- /dev/null +++ b/tests/unit/unittest/clangdocumentprocessors-test.cpp @@ -0,0 +1,194 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "clangiasyncjob.h" +#include "dummyclangipcclient.h" +#include "processevents-utilities.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "gtest-qt-printing.h" + +using testing::Eq; + +using namespace ClangBackEnd; + +namespace { + +class DocumentProcessors : public ::testing::Test +{ +protected: + void SetUp() override; + void TearDown() override; + + ClangBackEnd::JobRequest createJobRequest(ClangBackEnd::JobRequest::Type type) const; + + bool waitUntilAllJobsFinished(int timeOutInMs = 10000) const; + +protected: + ClangBackEnd::ProjectParts projects; + ClangBackEnd::UnsavedFiles unsavedFiles; + ClangBackEnd::Documents documents{projects, unsavedFiles}; + ClangBackEnd::Document document; + + DummyIpcClient dummyIpcClient; + + Utf8String filePath{Utf8StringLiteral(TESTDATA_DIR"/translationunits.cpp")}; + Utf8String projectPartId{Utf8StringLiteral("/path/to/projectfile")}; + + ClangBackEnd::JobRequest jobRequest; + ClangBackEnd::JobContext jobContext; + + ClangBackEnd::DocumentProcessors documentProcessors{documents, + unsavedFiles, + projects, + dummyIpcClient}; +}; + +TEST_F(DocumentProcessors, HasNoItemsInitially) +{ + ASSERT_TRUE(documentProcessors.processors().empty()); +} + +TEST_F(DocumentProcessors, CreateAddsADocumentProcessor) +{ + documentProcessors.create(document); + + ASSERT_THAT(documentProcessors.processors().size(), Eq(1)); +} + +TEST_F(DocumentProcessors, CreateReturnsDocumentProcessor) +{ + const DocumentProcessor documentProcessor = documentProcessors.create(document); + + ASSERT_THAT(documentProcessor.document(), Eq(document)); +} + +TEST_F(DocumentProcessors, CreateThrowsForAlreadyExisting) +{ + documentProcessors.create(document); + + ASSERT_THROW(documentProcessors.create(document), + ClangBackEnd::DocumentProcessorAlreadyExists); +} + +TEST_F(DocumentProcessors, Access) +{ + documentProcessors.create(document); + + const DocumentProcessor documentProcessor = documentProcessors.processor(document); + + ASSERT_THAT(documentProcessor.document(), Eq(document)); +} + +TEST_F(DocumentProcessors, AccessThrowsForNotExisting) +{ + ASSERT_THROW(documentProcessors.processor(document), + ClangBackEnd::DocumentProcessorDoesNotExist); +} + +TEST_F(DocumentProcessors, Remove) +{ + documentProcessors.create(document); + + documentProcessors.remove(document); + + ASSERT_TRUE(documentProcessors.processors().empty()); +} + +TEST_F(DocumentProcessors, RemoveThrowsForNotExisting) +{ + ASSERT_THROW(documentProcessors.remove(document), + ClangBackEnd::DocumentProcessorDoesNotExist); +} + +TEST_F(DocumentProcessors, ProcessEmpty) +{ + documentProcessors.create(document); + + const JobRequests jobsStarted = documentProcessors.process(); + + ASSERT_TRUE(jobsStarted.isEmpty()); +} + +TEST_F(DocumentProcessors, ProcessSingle) +{ + DocumentProcessor documentProcessor = documentProcessors.create(document); + const JobRequest jobRequest = createJobRequest(JobRequest::Type::UpdateDocumentAnnotations); + documentProcessor.addJob(jobRequest); + + const JobRequests jobsStarted = documentProcessors.process(); + + ASSERT_THAT(jobsStarted.size(), 1); +} + +void DocumentProcessors::SetUp() +{ + projects.createOrUpdate({ProjectPartContainer(projectPartId)}); + + const QVector fileContainer{FileContainer(filePath, projectPartId)}; + document = documents.create(fileContainer).front(); + documents.setVisibleInEditors({filePath}); + documents.setUsedByCurrentEditor(filePath); +} + +void DocumentProcessors::TearDown() +{ + ASSERT_TRUE(waitUntilAllJobsFinished()); // QFuture/QFutureWatcher is implemented with events +} + +JobRequest DocumentProcessors::createJobRequest(JobRequest::Type type) const +{ + JobRequest jobRequest; + jobRequest.type = type; + jobRequest.requirements = JobRequest::requirementsForType(type); + jobRequest.filePath = filePath; + jobRequest.projectPartId = projectPartId; + jobRequest.unsavedFilesChangeTimePoint = unsavedFiles.lastChangeTimePoint(); + jobRequest.documentRevision = document.documentRevision(); + jobRequest.projectChangeTimePoint = projects.project(projectPartId).lastChangeTimePoint(); + + return jobRequest; +} + +bool DocumentProcessors::waitUntilAllJobsFinished(int timeOutInMs) const +{ + const auto noJobsRunningAnymore = [this](){ return documentProcessors.runningJobs().isEmpty(); }; + + return ProcessEventUtilities::processEventsUntilTrue(noJobsRunningAnymore, timeOutInMs); +} + +} // anonymous diff --git a/tests/unit/unittest/clangipcserver-test.cpp b/tests/unit/unittest/clangipcserver-test.cpp index 44e631323af..3aa7ed33f38 100644 --- a/tests/unit/unittest/clangipcserver-test.cpp +++ b/tests/unit/unittest/clangipcserver-test.cpp @@ -323,8 +323,8 @@ void ClangClangCodeModelServer::TearDown() bool ClangClangCodeModelServer::waitUntilAllJobsFinished(int timeOutInMs) { const auto noJobsRunningAnymore = [this]() { - return clangServer.jobsForTestOnly().runningJobs() == 0 - && clangServer.jobsForTestOnly().queue().size() == 0 + return clangServer.runningJobsForTestsOnly().isEmpty() + && clangServer.queueSizeForTestsOnly() == 0 && !clangServer.isTimerRunningForTestOnly(); }; diff --git a/tests/unit/unittest/clangjobs-test.cpp b/tests/unit/unittest/clangjobs-test.cpp index 3c642343351..d5ef57155b5 100644 --- a/tests/unit/unittest/clangjobs-test.cpp +++ b/tests/unit/unittest/clangjobs-test.cpp @@ -79,7 +79,7 @@ TEST_F(Jobs, ProcessEmptyQueue) const JobRequests jobsStarted = jobs.process(); ASSERT_THAT(jobsStarted.size(), Eq(0)); - ASSERT_THAT(jobs.runningJobs(), Eq(0)); + ASSERT_TRUE(jobs.runningJobs().isEmpty()); } TEST_F(Jobs, ProcessQueueWithSingleJob) @@ -89,7 +89,7 @@ TEST_F(Jobs, ProcessQueueWithSingleJob) const JobRequests jobsStarted = jobs.process(); ASSERT_THAT(jobsStarted.size(), Eq(1)); - ASSERT_THAT(jobs.runningJobs(), Eq(1)); + ASSERT_THAT(jobs.runningJobs().size(), Eq(1)); } TEST_F(Jobs, ProcessQueueUntilEmpty) @@ -130,7 +130,7 @@ void Jobs::TearDown() bool Jobs::waitUntilAllJobsFinished(int timeOutInMs) const { - const auto noJobsRunningAnymore = [this](){ return jobs.runningJobs() == 0; }; + const auto noJobsRunningAnymore = [this](){ return jobs.runningJobs().isEmpty(); }; return ProcessEventUtilities::processEventsUntilTrue(noJobsRunningAnymore, timeOutInMs); } @@ -138,7 +138,7 @@ bool Jobs::waitUntilAllJobsFinished(int timeOutInMs) const bool Jobs::waitUntilJobChainFinished(int timeOutInMs) const { const auto noJobsRunningAnymore = [this]() { - return jobs.runningJobs() == 0 && jobs.queue().isEmpty(); + return jobs.runningJobs().isEmpty() && jobs.queue().isEmpty(); }; return ProcessEventUtilities::processEventsUntilTrue(noJobsRunningAnymore, timeOutInMs); diff --git a/tests/unit/unittest/unittest.pro b/tests/unit/unittest/unittest.pro index 6c40fbb8ece..48bd0a59fa8 100644 --- a/tests/unit/unittest/unittest.pro +++ b/tests/unit/unittest/unittest.pro @@ -49,6 +49,8 @@ SOURCES += \ clangdiagnosticfilter-test.cpp \ clangdocuments-test.cpp \ clangdocument-test.cpp \ + clangdocumentprocessor-test.cpp \ + clangdocumentprocessors-test.cpp \ clangfixitoperation-test.cpp \ clangipcserver-test.cpp \ clangisdiagnosticrelatedtolocation-test.cpp \ From 1a426d9f0146a11f40686be4b929ccd3f8399b61 Mon Sep 17 00:00:00 2001 From: Nikolai Kosjar Date: Tue, 13 Sep 2016 10:41:22 +0200 Subject: [PATCH 06/27] Clang: Support second translation unit A TranslationUnit is owned by TranslationUnits now. TranslationUnits allows to add another TranslationUnit and to update/query the recently and previously parsed translation unit. This does not change any behavior yet. Change-Id: I8a2f0cc05d3e51bf739dd5d7c4da14b54147f3ab Reviewed-by: David Schulz --- .../ipcsource/clangbackend_global.h | 36 +++++ .../ipcsource/clangbackendclangipc-source.pri | 3 + .../clangbackend/ipcsource/clangdocument.cpp | 30 +++- .../clangbackend/ipcsource/clangdocument.h | 6 +- .../ipcsource/clangexceptions.cpp | 7 + .../clangbackend/ipcsource/clangexceptions.h | 6 + .../ipcsource/clangtranslationunit.cpp | 19 ++- .../ipcsource/clangtranslationunit.h | 6 +- .../ipcsource/clangtranslationunits.cpp | 135 +++++++++++++++++ .../ipcsource/clangtranslationunits.h | 80 ++++++++++ .../ipcsource/clangtranslationunitupdater.cpp | 4 +- .../ipcsource/clangtranslationunitupdater.h | 6 +- tests/unit/unittest/clangdocument-test.cpp | 25 +++ .../unittest/clangtranslationunits-test.cpp | 143 ++++++++++++++++++ tests/unit/unittest/cursor-test.cpp | 1 + .../unit/unittest/highlightingmarks-test.cpp | 1 + .../unittest/skippedsourceranges-test.cpp | 1 + tests/unit/unittest/sourcerange-test.cpp | 1 + .../unittest/translationunitupdater-test.cpp | 19 ++- tests/unit/unittest/unittest.pro | 1 + 20 files changed, 508 insertions(+), 22 deletions(-) create mode 100644 src/tools/clangbackend/ipcsource/clangbackend_global.h create mode 100644 src/tools/clangbackend/ipcsource/clangtranslationunits.cpp create mode 100644 src/tools/clangbackend/ipcsource/clangtranslationunits.h create mode 100644 tests/unit/unittest/clangtranslationunits-test.cpp diff --git a/src/tools/clangbackend/ipcsource/clangbackend_global.h b/src/tools/clangbackend/ipcsource/clangbackend_global.h new file mode 100644 index 00000000000..ff17d1c1046 --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangbackend_global.h @@ -0,0 +1,36 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 + +namespace ClangBackEnd { + +enum class PreferredTranslationUnit +{ + RecentlyParsed, + PreviouslyParsed, +}; + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri b/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri index dc468470f56..f79c1dcab89 100644 --- a/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri +++ b/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri @@ -29,6 +29,7 @@ HEADERS += $$PWD/clangcodemodelserver.h \ $$PWD/highlightingmarksiterator.h \ $$PWD/utf8positionfromlinecolumn.h \ $$PWD/clangasyncjob.h \ + $$PWD/clangbackend_global.h \ $$PWD/clangcompletecodejob.h \ $$PWD/clangcreateinitialdocumentpreamblejob.h \ $$PWD/clangfilepath.h \ @@ -44,6 +45,7 @@ HEADERS += $$PWD/clangcodemodelserver.h \ $$PWD/clangexceptions.h \ $$PWD/clangdocumentprocessor.h \ $$PWD/clangdocumentprocessors.h \ + $$PWD/clangtranslationunits.h SOURCES += $$PWD/clangcodemodelserver.cpp \ $$PWD/codecompleter.cpp \ @@ -85,3 +87,4 @@ SOURCES += $$PWD/clangcodemodelserver.cpp \ $$PWD/clangexceptions.cpp \ $$PWD/clangdocumentprocessor.cpp \ $$PWD/clangdocumentprocessors.cpp \ + $$PWD/clangtranslationunits.cpp diff --git a/src/tools/clangbackend/ipcsource/clangdocument.cpp b/src/tools/clangbackend/ipcsource/clangdocument.cpp index 3e9b6971abf..af45cc43f5b 100644 --- a/src/tools/clangbackend/ipcsource/clangdocument.cpp +++ b/src/tools/clangbackend/ipcsource/clangdocument.cpp @@ -32,6 +32,7 @@ #include "projectpart.h" #include "clangexceptions.h" #include "clangtranslationunit.h" +#include "clangtranslationunits.h" #include "clangtranslationunitupdater.h" #include "unsavedfiles.h" #include "unsavedfile.h" @@ -64,8 +65,7 @@ public: ProjectPart projectPart; time_point lastProjectPartChangeTimePoint; - CXTranslationUnit translationUnit = nullptr; - CXIndex index = nullptr; + TranslationUnits translationUnits; QSet dependedFilePaths; @@ -86,15 +86,15 @@ DocumentData::DocumentData(const Utf8String &filePath, fileArguments(fileArguments), projectPart(projectPart), lastProjectPartChangeTimePoint(std::chrono::steady_clock::now()), + translationUnits(filePath), needsToBeReparsedChangeTimePoint(lastProjectPartChangeTimePoint) { dependedFilePaths.insert(filePath); + translationUnits.createAndAppend(); } DocumentData::~DocumentData() { - clang_disposeTranslationUnit(translationUnit); - clang_disposeIndex(index); } Document::Document(const Utf8String &filePath, @@ -282,8 +282,13 @@ TranslationUnitUpdateInput Document::createUpdateInput() const TranslationUnitUpdater Document::createUpdater() const { + TranslationUnit unit = translationUnit(); + const TranslationUnitUpdateInput updateInput = createUpdateInput(); - TranslationUnitUpdater updater(d->index, d->translationUnit, updateInput); + TranslationUnitUpdater updater(unit.id(), + unit.cxIndex(), + unit.cxTranslationUnit(), + updateInput); return updater; } @@ -304,9 +309,13 @@ void Document::incorporateUpdaterResult(const TranslationUnitUpdateResult &resul if (result.hasParsed()) d->lastProjectPartChangeTimePoint = result.parseTimePoint; - if (result.hasParsed() || result.hasReparsed()) + if (result.hasParsed() || result.hasReparsed()) { d->dependedFilePaths = result.dependedOnFilePaths; + const time_point timePoint = qMax(result.parseTimePoint, result.reparseTimePoint); + d->translationUnits.updateParseTimePoint(result.translationUnitId, timePoint); + } + d->documents.addWatchedFiles(d->dependedFilePaths); if (result.hasReparsed() @@ -315,11 +324,16 @@ void Document::incorporateUpdaterResult(const TranslationUnitUpdateResult &resul } } -TranslationUnit Document::translationUnit() const +TranslationUnit Document::translationUnit(PreferredTranslationUnit preferredTranslationUnit) const { checkIfNull(); - return TranslationUnit(d->filePath, d->index, d->translationUnit); + return d->translationUnits.get(preferredTranslationUnit); +} + +TranslationUnits &Document::translationUnits() const +{ + return d->translationUnits; } void Document::parse() const diff --git a/src/tools/clangbackend/ipcsource/clangdocument.h b/src/tools/clangbackend/ipcsource/clangdocument.h index 55c1bbb4d48..712b10d6dea 100644 --- a/src/tools/clangbackend/ipcsource/clangdocument.h +++ b/src/tools/clangbackend/ipcsource/clangdocument.h @@ -27,6 +27,7 @@ #include "clangtranslationunitupdater.h" +#include "clangbackend_global.h" #include "clangtranslationunit.h" #include @@ -44,6 +45,7 @@ class Utf8String; namespace ClangBackEnd { class TranslationUnit; +class TranslationUnits; class DocumentData; class TranslationUnitUpdateResult; class ProjectPart; @@ -104,7 +106,9 @@ public: TranslationUnitUpdateInput createUpdateInput() const; void incorporateUpdaterResult(const TranslationUnitUpdateResult &result) const; - TranslationUnit translationUnit() const; + TranslationUnit translationUnit(PreferredTranslationUnit preferredTranslationUnit + = PreferredTranslationUnit::RecentlyParsed) const; + TranslationUnits &translationUnits() const; public: // for tests void parse() const; diff --git a/src/tools/clangbackend/ipcsource/clangexceptions.cpp b/src/tools/clangbackend/ipcsource/clangexceptions.cpp index 4a85df4129a..187ce98d1a1 100644 --- a/src/tools/clangbackend/ipcsource/clangexceptions.cpp +++ b/src/tools/clangbackend/ipcsource/clangexceptions.cpp @@ -94,4 +94,11 @@ DocumentProcessorDoesNotExist::DocumentProcessorDoesNotExist(const Utf8String &f + Utf8StringLiteral("' does not exist!"); } +TranslationUnitDoesNotExist::TranslationUnitDoesNotExist(const Utf8String &filePath) +{ + m_info += Utf8StringLiteral("TranslationUnit for file '") + + filePath + + Utf8StringLiteral("' does not exist."); +} + } // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangexceptions.h b/src/tools/clangbackend/ipcsource/clangexceptions.h index 1fbedccb38b..ffc1c09da14 100644 --- a/src/tools/clangbackend/ipcsource/clangexceptions.h +++ b/src/tools/clangbackend/ipcsource/clangexceptions.h @@ -88,4 +88,10 @@ public: const Utf8String &projectPartId); }; +class TranslationUnitDoesNotExist : public ClangBaseException +{ +public: + TranslationUnitDoesNotExist(const Utf8String &filePath); +}; + } // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangtranslationunit.cpp b/src/tools/clangbackend/ipcsource/clangtranslationunit.cpp index 37bdf088912..2b963b21ca4 100644 --- a/src/tools/clangbackend/ipcsource/clangtranslationunit.cpp +++ b/src/tools/clangbackend/ipcsource/clangtranslationunit.cpp @@ -38,10 +38,12 @@ namespace ClangBackEnd { -TranslationUnit::TranslationUnit(const Utf8String &filepath, +TranslationUnit::TranslationUnit(const Utf8String &id, + const Utf8String &filepath, CXIndex &cxIndex, CXTranslationUnit &cxTranslationUnit) - : m_filePath(filepath) + : m_id(id) + , m_filePath(filepath) , m_cxIndex(cxIndex) , m_cxTranslationUnit(cxTranslationUnit) { @@ -49,7 +51,12 @@ TranslationUnit::TranslationUnit(const Utf8String &filepath, bool TranslationUnit::isNull() const { - return !m_cxTranslationUnit || !m_cxIndex || m_filePath.isEmpty(); + return !m_cxTranslationUnit || !m_cxIndex || m_filePath.isEmpty() || m_id.isEmpty(); +} + +Utf8String TranslationUnit::id() const +{ + return m_id; } Utf8String TranslationUnit::filePath() const @@ -70,7 +77,7 @@ CXTranslationUnit &TranslationUnit::cxTranslationUnit() const TranslationUnitUpdateResult TranslationUnit::update( const TranslationUnitUpdateInput &parseInput) const { - TranslationUnitUpdater updater(cxIndex(), cxTranslationUnit(), parseInput); + TranslationUnitUpdater updater(id(), cxIndex(), cxTranslationUnit(), parseInput); return updater.update(TranslationUnitUpdater::UpdateMode::AsNeeded); } @@ -78,7 +85,7 @@ TranslationUnitUpdateResult TranslationUnit::update( TranslationUnitUpdateResult TranslationUnit::parse( const TranslationUnitUpdateInput &parseInput) const { - TranslationUnitUpdater updater(cxIndex(), cxTranslationUnit(), parseInput); + TranslationUnitUpdater updater(id(), cxIndex(), cxTranslationUnit(), parseInput); return updater.update(TranslationUnitUpdater::UpdateMode::ParseIfNeeded); } @@ -86,7 +93,7 @@ TranslationUnitUpdateResult TranslationUnit::parse( TranslationUnitUpdateResult TranslationUnit::reparse( const TranslationUnitUpdateInput &parseInput) const { - TranslationUnitUpdater updater(cxIndex(), cxTranslationUnit(), parseInput); + TranslationUnitUpdater updater(id(), cxIndex(), cxTranslationUnit(), parseInput); return updater.update(TranslationUnitUpdater::UpdateMode::ForceReparse); } diff --git a/src/tools/clangbackend/ipcsource/clangtranslationunit.h b/src/tools/clangbackend/ipcsource/clangtranslationunit.h index 3b7e81f6180..cf65dddc3a0 100644 --- a/src/tools/clangbackend/ipcsource/clangtranslationunit.h +++ b/src/tools/clangbackend/ipcsource/clangtranslationunit.h @@ -57,12 +57,15 @@ public: }; public: - TranslationUnit(const Utf8String &filePath, + TranslationUnit(const Utf8String &id, + const Utf8String &filePath, CXIndex &cxIndex, CXTranslationUnit &cxTranslationUnit); bool isNull() const; + Utf8String id() const; + Utf8String filePath() const; CXIndex &cxIndex() const; CXTranslationUnit &cxTranslationUnit() const; @@ -94,6 +97,7 @@ public: SkippedSourceRanges skippedSourceRanges() const; private: + const Utf8String m_id; const Utf8String m_filePath; CXIndex &m_cxIndex; CXTranslationUnit &m_cxTranslationUnit; diff --git a/src/tools/clangbackend/ipcsource/clangtranslationunits.cpp b/src/tools/clangbackend/ipcsource/clangtranslationunits.cpp new file mode 100644 index 00000000000..6992dbd1a67 --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangtranslationunits.cpp @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "clangtranslationunits.h" + +#include "clangexceptions.h" +#include "clangtranslationunit.h" + +#include +#include + +#include +#include +#include + +#include + +Q_LOGGING_CATEGORY(tuLog, "qtc.clangbackend.translationunits"); + +namespace ClangBackEnd { + +TranslationUnits::TranslationUnits(const Utf8String &filePath) + : m_filePath(filePath) +{ +} + +TranslationUnits::~TranslationUnits() +{ + foreach (const TranslationUnitData &unit, m_tuDatas) { + clang_disposeTranslationUnit(unit.cxTranslationUnit); + clang_disposeIndex(unit.cxIndex); + } +} + +TranslationUnit TranslationUnits::createAndAppend() +{ + const Utf8String id = Utf8String::fromByteArray(QUuid::createUuid().toByteArray()); + qCDebug(tuLog) << "Creating TranslationUnit" << id << "for" << QFileInfo(m_filePath).fileName(); + + m_tuDatas.append(TranslationUnitData(id)); + TranslationUnitData &translationUnitData = m_tuDatas.last(); + + return toTranslationUnit(translationUnitData); +} + +TranslationUnit TranslationUnits::get(PreferredTranslationUnit type) +{ + if (m_tuDatas.isEmpty()) + throw TranslationUnitDoesNotExist(m_filePath); + + if (m_tuDatas.size() == 1 || !areAllTranslationUnitsParsed()) + return toTranslationUnit(m_tuDatas.first()); + + return getPreferredTranslationUnit(type); +} + +void TranslationUnits::updateParseTimePoint(const Utf8String &translationUnitId, + time_point timePoint) +{ + TranslationUnitData &unit = findUnit(translationUnitId); + + QTC_CHECK(timePoint != time_point()); + unit.parseTimePoint = timePoint; + + qCDebug(tuLog) << "Updated" << translationUnitId << "for" << QFileInfo(m_filePath).fileName() + << "RecentlyParsed:" << get(PreferredTranslationUnit::RecentlyParsed).id() + << "PreviouslyParsed:" << get(PreferredTranslationUnit::PreviouslyParsed).id(); +} + +bool TranslationUnits::areAllTranslationUnitsParsed() const +{ + return Utils::allOf(m_tuDatas, [](const TranslationUnitData &unit) { + return unit.parseTimePoint != time_point(); + }); +} + +TranslationUnit TranslationUnits::getPreferredTranslationUnit(PreferredTranslationUnit type) +{ + using TuData = TranslationUnitData; + + const auto lessThan = [](const TuData &a, const TuData &b) { + return a.parseTimePoint < b.parseTimePoint; + }; + auto translationUnitData = type == PreferredTranslationUnit::RecentlyParsed + ? std::max_element(m_tuDatas.begin(), m_tuDatas.end(), lessThan) + : std::min_element(m_tuDatas.begin(), m_tuDatas.end(), lessThan); + + if (translationUnitData == m_tuDatas.end()) + throw TranslationUnitDoesNotExist(m_filePath); + + return toTranslationUnit(*translationUnitData); +} + +TranslationUnits::TranslationUnitData &TranslationUnits::findUnit( + const Utf8String &translationUnitId) +{ + for (TranslationUnitData &unit : m_tuDatas) { + if (translationUnitId == unit.id) + return unit; + } + + throw TranslationUnitDoesNotExist(m_filePath); +} + +TranslationUnit TranslationUnits::toTranslationUnit(TranslationUnits::TranslationUnitData &unit) +{ + return TranslationUnit(unit.id, + m_filePath, + unit.cxIndex, + unit.cxTranslationUnit); +} + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangtranslationunits.h b/src/tools/clangbackend/ipcsource/clangtranslationunits.h new file mode 100644 index 00000000000..b5bd1dc3d10 --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangtranslationunits.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "clangbackend_global.h" + +#include + +#include + +#include + +#include + +namespace ClangBackEnd { + +using time_point = std::chrono::steady_clock::time_point; + +class TranslationUnit; + +class TranslationUnits +{ +public: + class TranslationUnitData { + public: + TranslationUnitData(const Utf8String &id) + : id(id) + {} + + Utf8String id; + + CXTranslationUnit cxTranslationUnit = nullptr; + CXIndex cxIndex = nullptr; + + time_point parseTimePoint; + }; + +public: + TranslationUnits(const Utf8String &filePath); + ~TranslationUnits(); + + TranslationUnit createAndAppend(); + TranslationUnit get(PreferredTranslationUnit type = PreferredTranslationUnit::RecentlyParsed); + void updateParseTimePoint(const Utf8String &translationUnitId, time_point timePoint); + +private: + bool areAllTranslationUnitsParsed() const; + TranslationUnit getPreferredTranslationUnit(PreferredTranslationUnit type); + TranslationUnitData &findUnit(const Utf8String &translationUnitId); + TranslationUnit toTranslationUnit(TranslationUnitData &unit); + +private: + Utf8String m_filePath; + QList m_tuDatas; +}; + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangtranslationunitupdater.cpp b/src/tools/clangbackend/ipcsource/clangtranslationunitupdater.cpp index 51e34dc53c8..dec3334be47 100644 --- a/src/tools/clangbackend/ipcsource/clangtranslationunitupdater.cpp +++ b/src/tools/clangbackend/ipcsource/clangtranslationunitupdater.cpp @@ -40,13 +40,15 @@ static bool isVerboseModeEnabled() namespace ClangBackEnd { -TranslationUnitUpdater::TranslationUnitUpdater(CXIndex &index, +TranslationUnitUpdater::TranslationUnitUpdater(const Utf8String translationUnitId, + CXIndex &index, CXTranslationUnit &cxTranslationUnit, const TranslationUnitUpdateInput &updateData) : m_cxIndex(index) , m_cxTranslationUnit(cxTranslationUnit) , m_in(updateData) { + m_out.translationUnitId = translationUnitId; } TranslationUnitUpdateResult TranslationUnitUpdater::update(UpdateMode mode) diff --git a/src/tools/clangbackend/ipcsource/clangtranslationunitupdater.h b/src/tools/clangbackend/ipcsource/clangtranslationunitupdater.h index 6802daf7eb6..8f34339111b 100644 --- a/src/tools/clangbackend/ipcsource/clangtranslationunitupdater.h +++ b/src/tools/clangbackend/ipcsource/clangtranslationunitupdater.h @@ -63,8 +63,9 @@ public: { return reparseTimePoint != time_point(); } public: - bool hasParseOrReparseFailed = false; + Utf8String translationUnitId; + bool hasParseOrReparseFailed = false; time_point parseTimePoint; time_point reparseTimePoint; time_point needsToBeReparsedChangeTimePoint; @@ -81,7 +82,8 @@ public: }; public: - TranslationUnitUpdater(CXIndex &index, + TranslationUnitUpdater(const Utf8String translationUnitId, + CXIndex &index, CXTranslationUnit &cxTranslationUnit, const TranslationUnitUpdateInput &in); diff --git a/tests/unit/unittest/clangdocument-test.cpp b/tests/unit/unittest/clangdocument-test.cpp index 010964fb2fd..8ec6a608cc8 100644 --- a/tests/unit/unittest/clangdocument-test.cpp +++ b/tests/unit/unittest/clangdocument-test.cpp @@ -27,6 +27,8 @@ #include #include +#include +#include #include #include #include @@ -56,6 +58,8 @@ using ClangBackEnd::ProjectPart; using ClangBackEnd::ProjectPartContainer; using ClangBackEnd::Documents; using ClangBackEnd::TranslationUnitUpdateResult; +using ClangBackEnd::TranslationUnit; +using ClangBackEnd::TranslationUnits; using testing::IsNull; using testing::NotNull; @@ -332,6 +336,7 @@ TEST_F(Document, IncorporateUpdaterResultResetsDirtyness) TranslationUnitUpdateResult result; result.reparseTimePoint = std::chrono::steady_clock::now(); result.needsToBeReparsedChangeTimePoint = document.isNeededReparseChangeTimePoint(); + result.translationUnitId = document.translationUnit().id(); document.incorporateUpdaterResult(result); @@ -343,6 +348,7 @@ TEST_F(Document, IncorporateUpdaterResultDoesNotResetDirtynessIfItWasChanged) TranslationUnitUpdateResult result; result.reparseTimePoint = std::chrono::steady_clock::now(); result.needsToBeReparsedChangeTimePoint = std::chrono::steady_clock::now(); + result.translationUnitId = document.translationUnit().id(); document.setDirtyIfDependencyIsMet(document.filePath()); document.incorporateUpdaterResult(result); @@ -350,6 +356,25 @@ TEST_F(Document, IncorporateUpdaterResultDoesNotResetDirtynessIfItWasChanged) ASSERT_TRUE(document.isNeedingReparse()); } +TEST_F(Document, IncorporateUpdaterResultUpdatesTranslationUnitsReparseTimePoint) +{ + TranslationUnits &translationUnits = document.translationUnits(); + const TranslationUnit initialTranslationUnit = translationUnits.get(); + translationUnits.updateParseTimePoint(initialTranslationUnit.id(), std::chrono::steady_clock::now()); + const TranslationUnit alternativeTranslationUnit = translationUnits.createAndAppend(); + translationUnits.updateParseTimePoint(alternativeTranslationUnit.id(), std::chrono::steady_clock::now()); + TranslationUnitUpdateResult result; + result.reparseTimePoint = std::chrono::steady_clock::now(); + result.needsToBeReparsedChangeTimePoint = std::chrono::steady_clock::now(); + result.translationUnitId = initialTranslationUnit.id(); + document.setDirtyIfDependencyIsMet(document.filePath()); + ASSERT_THAT(translationUnits.get().id(), Eq(alternativeTranslationUnit.id())); + + document.incorporateUpdaterResult(result); + + ASSERT_THAT(translationUnits.get().id(), Eq(initialTranslationUnit.id())); +} + void Document::SetUp() { projects.createOrUpdate({ProjectPartContainer(projectPartId)}); diff --git a/tests/unit/unittest/clangtranslationunits-test.cpp b/tests/unit/unittest/clangtranslationunits-test.cpp new file mode 100644 index 00000000000..bf039338aa1 --- /dev/null +++ b/tests/unit/unittest/clangtranslationunits-test.cpp @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include "gtest-qt-printing.h" + +using ClangBackEnd::TranslationUnit; +using ClangBackEnd::TranslationUnits; +using ClangBackEnd::TranslationUnitDoesNotExist; +using ClangBackEnd::PreferredTranslationUnit; + +using testing::Eq; + +namespace { + +class TranslationUnits : public ::testing::Test +{ +protected: + Utf8String someFilePath = Utf8StringLiteral("someFilePath"); + ClangBackEnd::TranslationUnits translationUnits{someFilePath}; +}; + +TEST_F(TranslationUnits, CreatedUnitIsNull) +{ + TranslationUnit translationUnit = translationUnits.createAndAppend(); + + ASSERT_TRUE(translationUnit.isNull()); +} + +TEST_F(TranslationUnits, GetThrowsForNotExisting) +{ + ASSERT_THROW(translationUnits.get(), TranslationUnitDoesNotExist); +} + +TEST_F(TranslationUnits, GetForSingleTranslationUnit) +{ + const TranslationUnit created = translationUnits.createAndAppend(); + + const TranslationUnit queried = translationUnits.get(); + + ASSERT_THAT(queried.id(), Eq(created.id())); +} + +TEST_F(TranslationUnits, GetFirstForMultipleTranslationUnits) +{ + const TranslationUnit created1 = translationUnits.createAndAppend(); + translationUnits.createAndAppend(); + + const TranslationUnit queried = translationUnits.get(); + + ASSERT_THAT(queried.id(), Eq(created1.id())); +} + +TEST_F(TranslationUnits, GetFirstForMultipleTranslationUnitsAndOnlyFirstParsed) +{ + const TranslationUnit created1 = translationUnits.createAndAppend(); + translationUnits.updateParseTimePoint(created1.id(), std::chrono::steady_clock::now()); + translationUnits.createAndAppend(); + + const TranslationUnit queried = translationUnits.get(); + + ASSERT_THAT(queried.id(), Eq(created1.id())); +} + +TEST_F(TranslationUnits, GetFirstForMultipleTranslationUnitsAndOnlySecondParsed) +{ + const TranslationUnit created1 = translationUnits.createAndAppend(); + const TranslationUnit created2 = translationUnits.createAndAppend(); + translationUnits.updateParseTimePoint(created2.id(), std::chrono::steady_clock::now()); + + const TranslationUnit queried = translationUnits.get(); + + ASSERT_THAT(queried.id(), Eq(created1.id())); +} + +TEST_F(TranslationUnits, GetRecentForMultipleTranslationUnits) +{ + const TranslationUnit created1 = translationUnits.createAndAppend(); + translationUnits.updateParseTimePoint(created1.id(), std::chrono::steady_clock::now()); + const TranslationUnit created2 = translationUnits.createAndAppend(); + translationUnits.updateParseTimePoint(created2.id(), std::chrono::steady_clock::now()); + + const TranslationUnit queried = translationUnits.get(PreferredTranslationUnit::RecentlyParsed); + + ASSERT_THAT(queried.id(), Eq(created2.id())); +} + +TEST_F(TranslationUnits, GetPreviousForMultipleTranslationUnits) +{ + const TranslationUnit created1 = translationUnits.createAndAppend(); + translationUnits.updateParseTimePoint(created1.id(), std::chrono::steady_clock::now()); + const TranslationUnit created2 = translationUnits.createAndAppend(); + translationUnits.updateParseTimePoint(created2.id(), std::chrono::steady_clock::now()); + + const TranslationUnit queried = translationUnits.get(PreferredTranslationUnit::PreviouslyParsed); + + ASSERT_THAT(queried.id(), Eq(created1.id())); +} + +TEST_F(TranslationUnits, UpdateThrowsForNotExisting) +{ + ClangBackEnd::TranslationUnits otherTranslationUnits{someFilePath}; + const TranslationUnit translationUnit = otherTranslationUnits.createAndAppend(); + + ASSERT_THROW(translationUnits.updateParseTimePoint(translationUnit.id(), std::chrono::steady_clock::now()), + TranslationUnitDoesNotExist); +} + +} // anonymous namespace diff --git a/tests/unit/unittest/cursor-test.cpp b/tests/unit/unittest/cursor-test.cpp index 6fa4249c725..bba963be869 100644 --- a/tests/unit/unittest/cursor-test.cpp +++ b/tests/unit/unittest/cursor-test.cpp @@ -68,6 +68,7 @@ struct Data { {}, documents}; TranslationUnit translationUnit{filePath, + filePath, document.translationUnit().cxIndex(), document.translationUnit().cxTranslationUnit()}; }; diff --git a/tests/unit/unittest/highlightingmarks-test.cpp b/tests/unit/unittest/highlightingmarks-test.cpp index de79d379fd9..577fa66fdd5 100644 --- a/tests/unit/unittest/highlightingmarks-test.cpp +++ b/tests/unit/unittest/highlightingmarks-test.cpp @@ -111,6 +111,7 @@ struct Data { {}, documents}; TranslationUnit translationUnit{filePath, + filePath, document.translationUnit().cxIndex(), document.translationUnit().cxTranslationUnit()}; }; diff --git a/tests/unit/unittest/skippedsourceranges-test.cpp b/tests/unit/unittest/skippedsourceranges-test.cpp index e775961a84b..1e1d9d4ee6f 100644 --- a/tests/unit/unittest/skippedsourceranges-test.cpp +++ b/tests/unit/unittest/skippedsourceranges-test.cpp @@ -99,6 +99,7 @@ struct Data { {}, documents}; TranslationUnit translationUnit{filePath, + filePath, document.translationUnit().cxIndex(), document.translationUnit().cxTranslationUnit()}; }; diff --git a/tests/unit/unittest/sourcerange-test.cpp b/tests/unit/unittest/sourcerange-test.cpp index a9adee64138..c50bb98dc21 100644 --- a/tests/unit/unittest/sourcerange-test.cpp +++ b/tests/unit/unittest/sourcerange-test.cpp @@ -103,6 +103,7 @@ struct Data { Utf8StringVector(), documents}; TranslationUnit translationUnit{filePath, + filePath, document.translationUnit().cxIndex(), document.translationUnit().cxTranslationUnit()}; diff --git a/tests/unit/unittest/translationunitupdater-test.cpp b/tests/unit/unittest/translationunitupdater-test.cpp index 40ca8909713..8a4e8ad0f60 100644 --- a/tests/unit/unittest/translationunitupdater-test.cpp +++ b/tests/unit/unittest/translationunitupdater-test.cpp @@ -33,6 +33,7 @@ using ClangBackEnd::TranslationUnitUpdater; using ClangBackEnd::TranslationUnitUpdateInput; using ClangBackEnd::TranslationUnitUpdateResult; +using testing::Eq; using testing::Gt; namespace { @@ -42,7 +43,8 @@ class TranslationUnitUpdater : public ::testing::Test protected: void TearDown() override; - ::TranslationUnitUpdater createUpdater(const TranslationUnitUpdateInput &input); + ::TranslationUnitUpdater createUpdater(const TranslationUnitUpdateInput &input, + const Utf8String &translationUnitId = Utf8String()); enum ReparseMode { SetReparseNeeded, DoNotSetReparseNeeded }; TranslationUnitUpdateInput createInput(ReparseMode reparseMode = DoNotSetReparseNeeded); @@ -73,6 +75,16 @@ TEST_F(TranslationUnitUpdater, ReparsesIfNeeded) ASSERT_TRUE(result.hasReparsed()); } +TEST_F(TranslationUnitUpdater, PropagatesTranslationUnitId) +{ + const Utf8String translationUnitId = Utf8StringLiteral("myId"); + ::TranslationUnitUpdater updater = createUpdater(createInput(SetReparseNeeded), translationUnitId); + + TranslationUnitUpdateResult result = updater.update(::TranslationUnitUpdater::UpdateMode::AsNeeded); + + ASSERT_THAT(result.translationUnitId, Eq(translationUnitId)); +} + TEST_F(TranslationUnitUpdater, UpdatesParseTimePoint) { ::TranslationUnitUpdater updater = createUpdater(createInput()); @@ -111,9 +123,10 @@ void TranslationUnitUpdater::TearDown() } ::TranslationUnitUpdater -TranslationUnitUpdater::createUpdater(const TranslationUnitUpdateInput &input) +TranslationUnitUpdater::createUpdater(const TranslationUnitUpdateInput &input, + const Utf8String &translationUnitId) { - return ::TranslationUnitUpdater(cxIndex, cxTranslationUnit, input); + return ::TranslationUnitUpdater(translationUnitId, cxIndex, cxTranslationUnit, input); } TranslationUnitUpdateInput diff --git a/tests/unit/unittest/unittest.pro b/tests/unit/unittest/unittest.pro index 48bd0a59fa8..168047cca55 100644 --- a/tests/unit/unittest/unittest.pro +++ b/tests/unit/unittest/unittest.pro @@ -58,6 +58,7 @@ SOURCES += \ clangjobs-test.cpp \ clangrequestdocumentannotationsjob-test.cpp \ clangstring-test.cpp \ + clangtranslationunits-test.cpp \ clangupdatedocumentannotationsjob-test.cpp \ codecompletionsextractor-test.cpp \ codecompletion-test.cpp \ From 8d443b40b7c89ab252ebab47c3d6a8c89943335a Mon Sep 17 00:00:00 2001 From: Nikolai Kosjar Date: Thu, 15 Sep 2016 11:56:25 +0200 Subject: [PATCH 07/27] Clang: Extract long clock/time_point references Change-Id: If2790263e9a314f27762c57cf6bf4ef67f93a84b Reviewed-by: David Schulz --- .../ipcsource/clangbackendclangipc-source.pri | 3 +- src/tools/clangbackend/ipcsource/clangclock.h | 36 +++++++++++++++++++ .../clangbackend/ipcsource/clangdocument.cpp | 14 ++++---- .../clangbackend/ipcsource/clangdocument.h | 7 ++-- .../clangbackend/ipcsource/clangjobrequest.h | 10 +++--- .../ipcsource/clangtranslationunits.cpp | 6 ++-- .../ipcsource/clangtranslationunits.h | 9 ++--- .../ipcsource/clangtranslationunitupdater.cpp | 4 +-- .../ipcsource/clangtranslationunitupdater.h | 18 +++++----- .../clangbackend/ipcsource/projectpart.cpp | 8 ++--- .../clangbackend/ipcsource/projectpart.h | 7 ++-- .../clangbackend/ipcsource/unsavedfiles.cpp | 8 ++--- .../clangbackend/ipcsource/unsavedfiles.h | 8 ++--- tests/unit/unittest/clangdocument-test.cpp | 22 ++++++------ .../unittest/clangtranslationunits-test.cpp | 17 +++++---- ...clangupdatedocumentannotationsjob-test.cpp | 2 +- tests/unit/unittest/projectpart-test.cpp | 6 ++-- .../unittest/translationunitupdater-test.cpp | 5 ++- 18 files changed, 109 insertions(+), 81 deletions(-) create mode 100644 src/tools/clangbackend/ipcsource/clangclock.h diff --git a/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri b/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri index f79c1dcab89..5fbe996d06b 100644 --- a/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri +++ b/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri @@ -45,7 +45,8 @@ HEADERS += $$PWD/clangcodemodelserver.h \ $$PWD/clangexceptions.h \ $$PWD/clangdocumentprocessor.h \ $$PWD/clangdocumentprocessors.h \ - $$PWD/clangtranslationunits.h + $$PWD/clangtranslationunits.h \ + $$PWD/clangclock.h \ SOURCES += $$PWD/clangcodemodelserver.cpp \ $$PWD/codecompleter.cpp \ diff --git a/src/tools/clangbackend/ipcsource/clangclock.h b/src/tools/clangbackend/ipcsource/clangclock.h new file mode 100644 index 00000000000..68d91478f74 --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangclock.h @@ -0,0 +1,36 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 + +namespace ClangBackEnd { + +using Clock = std::chrono::steady_clock; +using Duration = std::chrono::steady_clock::duration; +using TimePoint = std::chrono::steady_clock::time_point; + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangdocument.cpp b/src/tools/clangbackend/ipcsource/clangdocument.cpp index af45cc43f5b..49c1a3cef0a 100644 --- a/src/tools/clangbackend/ipcsource/clangdocument.cpp +++ b/src/tools/clangbackend/ipcsource/clangdocument.cpp @@ -63,14 +63,14 @@ public: const Utf8StringVector fileArguments; ProjectPart projectPart; - time_point lastProjectPartChangeTimePoint; + TimePoint lastProjectPartChangeTimePoint; TranslationUnits translationUnits; QSet dependedFilePaths; uint documentRevision = 0; - time_point needsToBeReparsedChangeTimePoint; + TimePoint needsToBeReparsedChangeTimePoint; bool hasParseOrReparseFailed = false; bool needsToBeReparsed = false; bool isUsedByCurrentEditor = false; @@ -85,7 +85,7 @@ DocumentData::DocumentData(const Utf8String &filePath, filePath(filePath), fileArguments(fileArguments), projectPart(projectPart), - lastProjectPartChangeTimePoint(std::chrono::steady_clock::now()), + lastProjectPartChangeTimePoint(Clock::now()), translationUnits(filePath), needsToBeReparsedChangeTimePoint(lastProjectPartChangeTimePoint) { @@ -183,7 +183,7 @@ const ProjectPart &Document::projectPart() const return d->projectPart; } -const time_point Document::lastProjectPartChangeTimePoint() const +const TimePoint Document::lastProjectPartChangeTimePoint() const { checkIfNull(); @@ -239,7 +239,7 @@ void Document::setIsVisibleInEditor(bool isVisibleInEditor) d->isVisibleInEditor = isVisibleInEditor; } -time_point Document::isNeededReparseChangeTimePoint() const +TimePoint Document::isNeededReparseChangeTimePoint() const { checkIfNull(); @@ -312,7 +312,7 @@ void Document::incorporateUpdaterResult(const TranslationUnitUpdateResult &resul if (result.hasParsed() || result.hasReparsed()) { d->dependedFilePaths = result.dependedOnFilePaths; - const time_point timePoint = qMax(result.parseTimePoint, result.reparseTimePoint); + const TimePoint timePoint = qMax(result.parseTimePoint, result.reparseTimePoint); d->translationUnits.updateParseTimePoint(result.translationUnitId, timePoint); } @@ -366,7 +366,7 @@ const QSet Document::dependedFilePaths() const void Document::setDirty() { - d->needsToBeReparsedChangeTimePoint = std::chrono::steady_clock::now(); + d->needsToBeReparsedChangeTimePoint = Clock::now(); d->needsToBeReparsed = true; } diff --git a/src/tools/clangbackend/ipcsource/clangdocument.h b/src/tools/clangbackend/ipcsource/clangdocument.h index 712b10d6dea..91112e9cddc 100644 --- a/src/tools/clangbackend/ipcsource/clangdocument.h +++ b/src/tools/clangbackend/ipcsource/clangdocument.h @@ -37,7 +37,6 @@ #include #include -#include #include class Utf8String; @@ -52,8 +51,6 @@ class ProjectPart; class FileContainer; class Documents; -using time_point = std::chrono::steady_clock::time_point; - class Document { public: @@ -87,7 +84,7 @@ public: Utf8String projectPartId() const; const ProjectPart &projectPart() const; - const time_point lastProjectPartChangeTimePoint() const; + const TimePoint lastProjectPartChangeTimePoint() const; bool isProjectPartOutdated() const; uint documentRevision() const; @@ -116,7 +113,7 @@ public: // for tests const QSet dependedFilePaths() const; TranslationUnitUpdater createUpdater() const; void setHasParseOrReparseFailed(bool hasFailed); - time_point isNeededReparseChangeTimePoint() const; + TimePoint isNeededReparseChangeTimePoint() const; private: void setDirty(); diff --git a/src/tools/clangbackend/ipcsource/clangjobrequest.h b/src/tools/clangbackend/ipcsource/clangjobrequest.h index ca75e4ec675..1979c51874e 100644 --- a/src/tools/clangbackend/ipcsource/clangjobrequest.h +++ b/src/tools/clangbackend/ipcsource/clangjobrequest.h @@ -25,18 +25,16 @@ #pragma once +#include "clangclock.h" + #include #include #include #include -#include - namespace ClangBackEnd { -using time_point = std::chrono::steady_clock::time_point; - class JobRequest { public: @@ -72,8 +70,8 @@ public: // General Utf8String filePath; Utf8String projectPartId; - time_point unsavedFilesChangeTimePoint; - time_point projectChangeTimePoint; + TimePoint unsavedFilesChangeTimePoint; + TimePoint projectChangeTimePoint; uint documentRevision = 0; // For code completion diff --git a/src/tools/clangbackend/ipcsource/clangtranslationunits.cpp b/src/tools/clangbackend/ipcsource/clangtranslationunits.cpp index 6992dbd1a67..394d653e483 100644 --- a/src/tools/clangbackend/ipcsource/clangtranslationunits.cpp +++ b/src/tools/clangbackend/ipcsource/clangtranslationunits.cpp @@ -77,11 +77,11 @@ TranslationUnit TranslationUnits::get(PreferredTranslationUnit type) } void TranslationUnits::updateParseTimePoint(const Utf8String &translationUnitId, - time_point timePoint) + TimePoint timePoint) { TranslationUnitData &unit = findUnit(translationUnitId); - QTC_CHECK(timePoint != time_point()); + QTC_CHECK(timePoint != TimePoint()); unit.parseTimePoint = timePoint; qCDebug(tuLog) << "Updated" << translationUnitId << "for" << QFileInfo(m_filePath).fileName() @@ -92,7 +92,7 @@ void TranslationUnits::updateParseTimePoint(const Utf8String &translationUnitId, bool TranslationUnits::areAllTranslationUnitsParsed() const { return Utils::allOf(m_tuDatas, [](const TranslationUnitData &unit) { - return unit.parseTimePoint != time_point(); + return unit.parseTimePoint != TimePoint(); }); } diff --git a/src/tools/clangbackend/ipcsource/clangtranslationunits.h b/src/tools/clangbackend/ipcsource/clangtranslationunits.h index b5bd1dc3d10..77f771cd98a 100644 --- a/src/tools/clangbackend/ipcsource/clangtranslationunits.h +++ b/src/tools/clangbackend/ipcsource/clangtranslationunits.h @@ -26,6 +26,7 @@ #pragma once #include "clangbackend_global.h" +#include "clangclock.h" #include @@ -33,12 +34,8 @@ #include -#include - namespace ClangBackEnd { -using time_point = std::chrono::steady_clock::time_point; - class TranslationUnit; class TranslationUnits @@ -55,7 +52,7 @@ public: CXTranslationUnit cxTranslationUnit = nullptr; CXIndex cxIndex = nullptr; - time_point parseTimePoint; + TimePoint parseTimePoint; }; public: @@ -64,7 +61,7 @@ public: TranslationUnit createAndAppend(); TranslationUnit get(PreferredTranslationUnit type = PreferredTranslationUnit::RecentlyParsed); - void updateParseTimePoint(const Utf8String &translationUnitId, time_point timePoint); + void updateParseTimePoint(const Utf8String &translationUnitId, TimePoint timePoint); private: bool areAllTranslationUnitsParsed() const; diff --git a/src/tools/clangbackend/ipcsource/clangtranslationunitupdater.cpp b/src/tools/clangbackend/ipcsource/clangtranslationunitupdater.cpp index dec3334be47..e65ce61fa1d 100644 --- a/src/tools/clangbackend/ipcsource/clangtranslationunitupdater.cpp +++ b/src/tools/clangbackend/ipcsource/clangtranslationunitupdater.cpp @@ -124,7 +124,7 @@ void TranslationUnitUpdater::createTranslationUnitIfNeeded() if (parseWasSuccessful()) { updateIncludeFilePaths(); - m_out.parseTimePoint = std::chrono::steady_clock::now(); + m_out.parseTimePoint = Clock::now(); } else { qWarning() << "Parsing" << m_in.filePath << "failed:" << errorCodeToText(m_parseErrorCode); @@ -153,7 +153,7 @@ void TranslationUnitUpdater::reparse() if (reparseWasSuccessful()) { updateIncludeFilePaths(); - m_out.reparseTimePoint = std::chrono::steady_clock::now(); + m_out.reparseTimePoint = Clock::now(); m_out.needsToBeReparsedChangeTimePoint = m_in.needsToBeReparsedChangeTimePoint; } else { qWarning() << "Reparsing" << m_in.filePath << "failed:" << m_reparseErrorCode; diff --git a/src/tools/clangbackend/ipcsource/clangtranslationunitupdater.h b/src/tools/clangbackend/ipcsource/clangtranslationunitupdater.h index 8f34339111b..547c6838bc3 100644 --- a/src/tools/clangbackend/ipcsource/clangtranslationunitupdater.h +++ b/src/tools/clangbackend/ipcsource/clangtranslationunitupdater.h @@ -25,6 +25,8 @@ #pragma once +#include "clangclock.h" + #include "commandlinearguments.h" #include "unsavedfiles.h" #include "utf8stringvector.h" @@ -33,18 +35,14 @@ #include -#include - namespace ClangBackEnd { -using time_point = std::chrono::steady_clock::time_point; - class TranslationUnitUpdateInput { public: bool parseNeeded = false; bool reparseNeeded = false; - time_point needsToBeReparsedChangeTimePoint; + TimePoint needsToBeReparsedChangeTimePoint; Utf8String filePath; Utf8StringVector fileArguments; @@ -57,18 +55,18 @@ public: class TranslationUnitUpdateResult { public: bool hasParsed() const - { return parseTimePoint != time_point(); } + { return parseTimePoint != TimePoint(); } bool hasReparsed() const - { return reparseTimePoint != time_point(); } + { return reparseTimePoint != TimePoint(); } public: Utf8String translationUnitId; bool hasParseOrReparseFailed = false; - time_point parseTimePoint; - time_point reparseTimePoint; - time_point needsToBeReparsedChangeTimePoint; + TimePoint parseTimePoint; + TimePoint reparseTimePoint; + TimePoint needsToBeReparsedChangeTimePoint; QSet dependedOnFilePaths; }; diff --git a/src/tools/clangbackend/ipcsource/projectpart.cpp b/src/tools/clangbackend/ipcsource/projectpart.cpp index b9060df2975..556aa4847c9 100644 --- a/src/tools/clangbackend/ipcsource/projectpart.cpp +++ b/src/tools/clangbackend/ipcsource/projectpart.cpp @@ -39,13 +39,13 @@ public: ~ProjectPartData(); public: - time_point lastChangeTimePoint; + TimePoint lastChangeTimePoint; Utf8StringVector arguments; Utf8String projectPartId; }; ProjectPartData::ProjectPartData(const Utf8String &projectPartId) - : lastChangeTimePoint(std::chrono::steady_clock::now()), + : lastChangeTimePoint(Clock::now()), projectPartId(projectPartId) { } @@ -111,14 +111,14 @@ const Utf8StringVector ProjectPart::arguments() const return d->arguments; } -const time_point &ProjectPart::lastChangeTimePoint() const +const TimePoint &ProjectPart::lastChangeTimePoint() const { return d->lastChangeTimePoint; } void ProjectPart::updateLastChangeTimePoint() { - d->lastChangeTimePoint = std::chrono::steady_clock::now(); + d->lastChangeTimePoint = Clock::now(); } bool operator==(const ProjectPart &first, const ProjectPart &second) diff --git a/src/tools/clangbackend/ipcsource/projectpart.h b/src/tools/clangbackend/ipcsource/projectpart.h index d359751c4cc..9cbea8e563a 100644 --- a/src/tools/clangbackend/ipcsource/projectpart.h +++ b/src/tools/clangbackend/ipcsource/projectpart.h @@ -25,9 +25,10 @@ #pragma once +#include "clangclock.h" + #include -#include #include class Utf8StringVector; @@ -37,8 +38,6 @@ namespace ClangBackEnd { class ProjectPartContainer; class ProjectPartData; -using time_point = std::chrono::steady_clock::time_point; - class ProjectPart { public: @@ -61,7 +60,7 @@ public: void setArguments(const Utf8StringVector &arguments_); const Utf8StringVector arguments() const; - const time_point &lastChangeTimePoint() const; + const TimePoint &lastChangeTimePoint() const; private: void updateLastChangeTimePoint(); diff --git a/src/tools/clangbackend/ipcsource/unsavedfiles.cpp b/src/tools/clangbackend/ipcsource/unsavedfiles.cpp index 5224e14e9a1..64f2afe3ff0 100644 --- a/src/tools/clangbackend/ipcsource/unsavedfiles.cpp +++ b/src/tools/clangbackend/ipcsource/unsavedfiles.cpp @@ -40,12 +40,12 @@ public: UnsavedFilesData(); public: - time_point lastChangeTimePoint; + TimePoint lastChangeTimePoint; QVector unsavedFiles; }; UnsavedFilesData::UnsavedFilesData() - : lastChangeTimePoint(std::chrono::steady_clock::now()) + : lastChangeTimePoint(Clock::now()) { } @@ -116,7 +116,7 @@ UnsavedFilesShallowArguments UnsavedFiles::shallowArguments() const return UnsavedFilesShallowArguments(*this); } -const time_point UnsavedFiles::lastChangeTimePoint() const +const TimePoint UnsavedFiles::lastChangeTimePoint() const { return d->lastChangeTimePoint; } @@ -154,7 +154,7 @@ void UnsavedFiles::addOrUpdateUnsavedFile(const FileContainer &fileContainer) void UnsavedFiles::updateLastChangeTimePoint() { - d->lastChangeTimePoint = std::chrono::steady_clock::now(); + d->lastChangeTimePoint = Clock::now(); } } // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/unsavedfiles.h b/src/tools/clangbackend/ipcsource/unsavedfiles.h index 541d6c3a6a9..eacab0bc342 100644 --- a/src/tools/clangbackend/ipcsource/unsavedfiles.h +++ b/src/tools/clangbackend/ipcsource/unsavedfiles.h @@ -25,6 +25,8 @@ #pragma once +#include "clangclock.h" + #include #include @@ -32,12 +34,8 @@ #include -#include - namespace ClangBackEnd { -using time_point = std::chrono::steady_clock::time_point; - class UnsavedFile; class UnsavedFilesData; class UnsavedFilesShallowArguments; @@ -61,7 +59,7 @@ public: UnsavedFilesShallowArguments shallowArguments() const; - const time_point lastChangeTimePoint() const; + const TimePoint lastChangeTimePoint() const; private: void updateUnsavedFileWithFileContainer(const FileContainer &fileContainer); diff --git a/tests/unit/unittest/clangdocument-test.cpp b/tests/unit/unittest/clangdocument-test.cpp index 8ec6a608cc8..a6a32dcf939 100644 --- a/tests/unit/unittest/clangdocument-test.cpp +++ b/tests/unit/unittest/clangdocument-test.cpp @@ -25,6 +25,7 @@ #include "googletest.h" +#include #include #include #include @@ -47,9 +48,10 @@ #include -#include #include +using ClangBackEnd::Clock; +using ClangBackEnd::Duration; using ClangBackEnd::FileContainer; using ClangBackEnd::FilePath; using ClangBackEnd::Document; @@ -165,7 +167,7 @@ TEST_F(Document, LastCommandLineArgumentIsFilePath) TEST_F(Document, TimeStampForProjectPartChangeIsUpdatedAsNewCxTranslationUnitIsGenerated) { auto lastChangeTimePoint = document.lastProjectPartChangeTimePoint(); - std::this_thread::sleep_for(std::chrono::steady_clock::duration(1)); + std::this_thread::sleep_for(Duration(1)); document.parse(); @@ -177,7 +179,7 @@ TEST_F(Document, TimeStampForProjectPartChangeIsUpdatedAsProjectPartIsCleared) ProjectPart projectPart = document.projectPart(); document.parse(); auto lastChangeTimePoint = document.lastProjectPartChangeTimePoint(); - std::this_thread::sleep_for(std::chrono::steady_clock::duration(1)); + std::this_thread::sleep_for(Duration(1)); projectPart.clear(); document.parse(); @@ -334,7 +336,7 @@ TEST_F(Document, IncorporateUpdaterResultResetsDirtyness) { document.setDirtyIfDependencyIsMet(document.filePath()); TranslationUnitUpdateResult result; - result.reparseTimePoint = std::chrono::steady_clock::now(); + result.reparseTimePoint = Clock::now(); result.needsToBeReparsedChangeTimePoint = document.isNeededReparseChangeTimePoint(); result.translationUnitId = document.translationUnit().id(); @@ -346,8 +348,8 @@ TEST_F(Document, IncorporateUpdaterResultResetsDirtyness) TEST_F(Document, IncorporateUpdaterResultDoesNotResetDirtynessIfItWasChanged) { TranslationUnitUpdateResult result; - result.reparseTimePoint = std::chrono::steady_clock::now(); - result.needsToBeReparsedChangeTimePoint = std::chrono::steady_clock::now(); + result.reparseTimePoint = Clock::now(); + result.needsToBeReparsedChangeTimePoint = Clock::now(); result.translationUnitId = document.translationUnit().id(); document.setDirtyIfDependencyIsMet(document.filePath()); @@ -360,12 +362,12 @@ TEST_F(Document, IncorporateUpdaterResultUpdatesTranslationUnitsReparseTimePoint { TranslationUnits &translationUnits = document.translationUnits(); const TranslationUnit initialTranslationUnit = translationUnits.get(); - translationUnits.updateParseTimePoint(initialTranslationUnit.id(), std::chrono::steady_clock::now()); + translationUnits.updateParseTimePoint(initialTranslationUnit.id(), Clock::now()); const TranslationUnit alternativeTranslationUnit = translationUnits.createAndAppend(); - translationUnits.updateParseTimePoint(alternativeTranslationUnit.id(), std::chrono::steady_clock::now()); + translationUnits.updateParseTimePoint(alternativeTranslationUnit.id(), Clock::now()); TranslationUnitUpdateResult result; - result.reparseTimePoint = std::chrono::steady_clock::now(); - result.needsToBeReparsedChangeTimePoint = std::chrono::steady_clock::now(); + result.reparseTimePoint = Clock::now(); + result.needsToBeReparsedChangeTimePoint = Clock::now(); result.translationUnitId = initialTranslationUnit.id(); document.setDirtyIfDependencyIsMet(document.filePath()); ASSERT_THAT(translationUnits.get().id(), Eq(alternativeTranslationUnit.id())); diff --git a/tests/unit/unittest/clangtranslationunits-test.cpp b/tests/unit/unittest/clangtranslationunits-test.cpp index bf039338aa1..ddd386b6bf0 100644 --- a/tests/unit/unittest/clangtranslationunits-test.cpp +++ b/tests/unit/unittest/clangtranslationunits-test.cpp @@ -31,13 +31,12 @@ #include -#include - #include #include #include #include "gtest-qt-printing.h" +using ClangBackEnd::Clock; using ClangBackEnd::TranslationUnit; using ClangBackEnd::TranslationUnits; using ClangBackEnd::TranslationUnitDoesNotExist; @@ -88,7 +87,7 @@ TEST_F(TranslationUnits, GetFirstForMultipleTranslationUnits) TEST_F(TranslationUnits, GetFirstForMultipleTranslationUnitsAndOnlyFirstParsed) { const TranslationUnit created1 = translationUnits.createAndAppend(); - translationUnits.updateParseTimePoint(created1.id(), std::chrono::steady_clock::now()); + translationUnits.updateParseTimePoint(created1.id(), Clock::now()); translationUnits.createAndAppend(); const TranslationUnit queried = translationUnits.get(); @@ -100,7 +99,7 @@ TEST_F(TranslationUnits, GetFirstForMultipleTranslationUnitsAndOnlySecondParsed) { const TranslationUnit created1 = translationUnits.createAndAppend(); const TranslationUnit created2 = translationUnits.createAndAppend(); - translationUnits.updateParseTimePoint(created2.id(), std::chrono::steady_clock::now()); + translationUnits.updateParseTimePoint(created2.id(), Clock::now()); const TranslationUnit queried = translationUnits.get(); @@ -110,9 +109,9 @@ TEST_F(TranslationUnits, GetFirstForMultipleTranslationUnitsAndOnlySecondParsed) TEST_F(TranslationUnits, GetRecentForMultipleTranslationUnits) { const TranslationUnit created1 = translationUnits.createAndAppend(); - translationUnits.updateParseTimePoint(created1.id(), std::chrono::steady_clock::now()); + translationUnits.updateParseTimePoint(created1.id(), Clock::now()); const TranslationUnit created2 = translationUnits.createAndAppend(); - translationUnits.updateParseTimePoint(created2.id(), std::chrono::steady_clock::now()); + translationUnits.updateParseTimePoint(created2.id(), Clock::now()); const TranslationUnit queried = translationUnits.get(PreferredTranslationUnit::RecentlyParsed); @@ -122,9 +121,9 @@ TEST_F(TranslationUnits, GetRecentForMultipleTranslationUnits) TEST_F(TranslationUnits, GetPreviousForMultipleTranslationUnits) { const TranslationUnit created1 = translationUnits.createAndAppend(); - translationUnits.updateParseTimePoint(created1.id(), std::chrono::steady_clock::now()); + translationUnits.updateParseTimePoint(created1.id(), Clock::now()); const TranslationUnit created2 = translationUnits.createAndAppend(); - translationUnits.updateParseTimePoint(created2.id(), std::chrono::steady_clock::now()); + translationUnits.updateParseTimePoint(created2.id(), Clock::now()); const TranslationUnit queried = translationUnits.get(PreferredTranslationUnit::PreviouslyParsed); @@ -136,7 +135,7 @@ TEST_F(TranslationUnits, UpdateThrowsForNotExisting) ClangBackEnd::TranslationUnits otherTranslationUnits{someFilePath}; const TranslationUnit translationUnit = otherTranslationUnits.createAndAppend(); - ASSERT_THROW(translationUnits.updateParseTimePoint(translationUnit.id(), std::chrono::steady_clock::now()), + ASSERT_THROW(translationUnits.updateParseTimePoint(translationUnit.id(), Clock::now()), TranslationUnitDoesNotExist); } diff --git a/tests/unit/unittest/clangupdatedocumentannotationsjob-test.cpp b/tests/unit/unittest/clangupdatedocumentannotationsjob-test.cpp index a76e0f8703c..291f2e298c3 100644 --- a/tests/unit/unittest/clangupdatedocumentannotationsjob-test.cpp +++ b/tests/unit/unittest/clangupdatedocumentannotationsjob-test.cpp @@ -97,7 +97,7 @@ TEST_F(UpdateDocumentAnnotationsJob, DontSendAnnotationsIfDocumentRevisionChange TEST_F(UpdateDocumentAnnotationsJob, UpdatesTranslationUnit) { - const time_point timePointBefore = document.lastProjectPartChangeTimePoint(); + const TimePoint timePointBefore = document.lastProjectPartChangeTimePoint(); const QSet dependendOnFilesBefore = document.dependedFilePaths(); job.setContext(jobContext); job.prepareAsyncRun(); diff --git a/tests/unit/unittest/projectpart-test.cpp b/tests/unit/unittest/projectpart-test.cpp index 7595cedbfc2..ec5d99f9b43 100644 --- a/tests/unit/unittest/projectpart-test.cpp +++ b/tests/unit/unittest/projectpart-test.cpp @@ -25,12 +25,12 @@ #include "googletest.h" +#include #include #include #include #include -#include #include using testing::ElementsAre; @@ -82,7 +82,7 @@ TEST(ProjectPart, TimeStampIsUpdatedAsArgumentChanged) { ClangBackEnd::ProjectPart project(Utf8StringLiteral("/tmp/blah.pro")); auto lastChangeTimePoint = project.lastChangeTimePoint(); - std::this_thread::sleep_for(std::chrono::steady_clock::duration(1)); + std::this_thread::sleep_for(ClangBackEnd::Duration(1)); project.setArguments(Utf8StringVector({Utf8StringLiteral("-O"), Utf8StringLiteral("-fast")})); @@ -163,7 +163,7 @@ TEST(ProjectPart, ProjectPartIsClearedAfterRemove) projects.createOrUpdate({projectContainer}); ClangBackEnd::ProjectPart project = *projects.findProjectPart(projectContainer.projectPartId()); const auto lastChangeTimePoint = project.lastChangeTimePoint(); - std::this_thread::sleep_for(std::chrono::steady_clock::duration(1)); + std::this_thread::sleep_for(ClangBackEnd::Duration(1)); projects.remove({projectContainer.projectPartId()}); diff --git a/tests/unit/unittest/translationunitupdater-test.cpp b/tests/unit/unittest/translationunitupdater-test.cpp index 8a4e8ad0f60..dd490cda98c 100644 --- a/tests/unit/unittest/translationunitupdater-test.cpp +++ b/tests/unit/unittest/translationunitupdater-test.cpp @@ -25,10 +25,13 @@ #include "googletest.h" +#include #include #include +using ClangBackEnd::Clock; +using ClangBackEnd::TimePoint; using ClangBackEnd::TranslationUnitUpdater; using ClangBackEnd::TranslationUnitUpdateInput; using ClangBackEnd::TranslationUnitUpdateResult; @@ -88,7 +91,7 @@ TEST_F(TranslationUnitUpdater, PropagatesTranslationUnitId) TEST_F(TranslationUnitUpdater, UpdatesParseTimePoint) { ::TranslationUnitUpdater updater = createUpdater(createInput()); - const std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); + const TimePoint now = Clock::now(); TranslationUnitUpdateResult result = updater.update(::TranslationUnitUpdater::UpdateMode::AsNeeded); From c8cec2dd0d7fa30e88c4e0fc0a6083d4bf2d7f05 Mon Sep 17 00:00:00 2001 From: Nikolai Kosjar Date: Tue, 13 Sep 2016 13:57:08 +0200 Subject: [PATCH 08/27] Clang: Base JobQueue on translation unit This enables a job per translation unit instead of per document. This does not change any behavior yet. Change-Id: Iafb8dab5da32b53dbb3010c16241bf89cbb81b38 Reviewed-by: David Schulz --- .../ipcsource/clangcompletecodejob.cpp | 10 ++--- .../ipcsource/clangcompletecodejob.h | 2 +- .../clangcreateinitialdocumentpreamblejob.cpp | 9 ++--- .../clangcreateinitialdocumentpreamblejob.h | 2 +- .../clangbackend/ipcsource/clangiasyncjob.h | 7 +++- .../clangbackend/ipcsource/clangjobqueue.cpp | 27 +++++++------- .../clangbackend/ipcsource/clangjobqueue.h | 5 +-- .../ipcsource/clangjobrequest.cpp | 20 +++++++++- .../clangbackend/ipcsource/clangjobrequest.h | 2 + .../clangbackend/ipcsource/clangjobs.cpp | 21 +++++------ src/tools/clangbackend/ipcsource/clangjobs.h | 3 +- .../clangrequestdocumentannotationsjob.cpp | 10 ++--- .../clangrequestdocumentannotationsjob.h | 2 +- .../clangupdatedocumentannotationsjob.cpp | 10 ++--- .../clangupdatedocumentannotationsjob.h | 2 +- tests/unit/unittest/clangjobqueue-test.cpp | 37 ++++++++++++++++--- tests/unit/unittest/clangjobs-test.cpp | 2 +- 17 files changed, 108 insertions(+), 63 deletions(-) diff --git a/src/tools/clangbackend/ipcsource/clangcompletecodejob.cpp b/src/tools/clangbackend/ipcsource/clangcompletecodejob.cpp index ce9d190bec0..8d13869bb8a 100644 --- a/src/tools/clangbackend/ipcsource/clangcompletecodejob.cpp +++ b/src/tools/clangbackend/ipcsource/clangcompletecodejob.cpp @@ -50,10 +50,10 @@ static CompleteCodeJob::AsyncResult runAsyncHelper(const TranslationUnit &transl return asyncResult; } -bool CompleteCodeJob::prepareAsyncRun() +IAsyncJob::AsyncPrepareResult CompleteCodeJob::prepareAsyncRun() { const JobRequest jobRequest = context().jobRequest; - QTC_ASSERT(jobRequest.type == JobRequest::Type::CompleteCode, return false); + QTC_ASSERT(jobRequest.type == JobRequest::Type::CompleteCode, return AsyncPrepareResult()); try { m_pinnedDocument = context().documentForJobRequest(); @@ -65,14 +65,12 @@ bool CompleteCodeJob::prepareAsyncRun() setRunner([translationUnit, unsavedFiles, line, column]() { return runAsyncHelper(translationUnit, unsavedFiles, line, column); }); - + return AsyncPrepareResult{translationUnit.id()}; } catch (const std::exception &exception) { qWarning() << "Error in CompleteCodeJob::prepareAsyncRun:" << exception.what(); - return false; + return AsyncPrepareResult(); } - - return true; } void CompleteCodeJob::finalizeAsyncRun() diff --git a/src/tools/clangbackend/ipcsource/clangcompletecodejob.h b/src/tools/clangbackend/ipcsource/clangcompletecodejob.h index 30c0bb8e3f9..142988d7403 100644 --- a/src/tools/clangbackend/ipcsource/clangcompletecodejob.h +++ b/src/tools/clangbackend/ipcsource/clangcompletecodejob.h @@ -43,7 +43,7 @@ class CompleteCodeJob : public AsyncJob public: using AsyncResult = CompleteCodeJobResult; - bool prepareAsyncRun() override; + AsyncPrepareResult prepareAsyncRun() override; void finalizeAsyncRun() override; private: diff --git a/src/tools/clangbackend/ipcsource/clangcreateinitialdocumentpreamblejob.cpp b/src/tools/clangbackend/ipcsource/clangcreateinitialdocumentpreamblejob.cpp index dd5e1f267f8..f24a3978e9e 100644 --- a/src/tools/clangbackend/ipcsource/clangcreateinitialdocumentpreamblejob.cpp +++ b/src/tools/clangbackend/ipcsource/clangcreateinitialdocumentpreamblejob.cpp @@ -39,10 +39,10 @@ static void runAsyncHelper(const TranslationUnit &translationUnit, translationUnit.reparse(translationUnitUpdateInput); } -bool CreateInitialDocumentPreambleJob::prepareAsyncRun() +IAsyncJob::AsyncPrepareResult CreateInitialDocumentPreambleJob::prepareAsyncRun() { const JobRequest jobRequest = context().jobRequest; - QTC_ASSERT(jobRequest.type == JobRequest::Type::CreateInitialDocumentPreamble, return false); + QTC_ASSERT(jobRequest.type == JobRequest::Type::CreateInitialDocumentPreamble, return AsyncPrepareResult()); try { m_pinnedDocument = context().documentForJobRequest(); @@ -53,14 +53,13 @@ bool CreateInitialDocumentPreambleJob::prepareAsyncRun() setRunner([translationUnit, updateInput]() { return runAsyncHelper(translationUnit, updateInput); }); + return AsyncPrepareResult{translationUnit.id()}; } catch (const std::exception &exception) { qWarning() << "Error in CreateInitialDocumentPreambleJob::prepareAsyncRun:" << exception.what(); - return false; + return AsyncPrepareResult(); } - - return true; } void CreateInitialDocumentPreambleJob::finalizeAsyncRun() diff --git a/src/tools/clangbackend/ipcsource/clangcreateinitialdocumentpreamblejob.h b/src/tools/clangbackend/ipcsource/clangcreateinitialdocumentpreamblejob.h index 21fc875982b..570b746460e 100644 --- a/src/tools/clangbackend/ipcsource/clangcreateinitialdocumentpreamblejob.h +++ b/src/tools/clangbackend/ipcsource/clangcreateinitialdocumentpreamblejob.h @@ -33,7 +33,7 @@ namespace ClangBackEnd { class CreateInitialDocumentPreambleJob : public AsyncJob { public: - bool prepareAsyncRun() override; + AsyncPrepareResult prepareAsyncRun() override; void finalizeAsyncRun() override; private: diff --git a/src/tools/clangbackend/ipcsource/clangiasyncjob.h b/src/tools/clangbackend/ipcsource/clangiasyncjob.h index dcc7df1c16f..98f61e857ab 100644 --- a/src/tools/clangbackend/ipcsource/clangiasyncjob.h +++ b/src/tools/clangbackend/ipcsource/clangiasyncjob.h @@ -41,6 +41,11 @@ class IAsyncJob public: static IAsyncJob *create(JobRequest::Type type); + struct AsyncPrepareResult { + operator bool() const { return !translationUnitId.isEmpty(); } + Utf8String translationUnitId; + }; + public: IAsyncJob(); virtual ~IAsyncJob(); @@ -52,7 +57,7 @@ public: FinishedHandler finishedHandler() const; void setFinishedHandler(const FinishedHandler &finishedHandler); - virtual bool prepareAsyncRun() = 0; + virtual AsyncPrepareResult prepareAsyncRun() = 0; virtual QFuture runAsync() = 0; virtual void finalizeAsyncRun() = 0; diff --git a/src/tools/clangbackend/ipcsource/clangjobqueue.cpp b/src/tools/clangbackend/ipcsource/clangjobqueue.cpp index 1beb1cdace6..cff2bd3bc05 100644 --- a/src/tools/clangbackend/ipcsource/clangjobqueue.cpp +++ b/src/tools/clangbackend/ipcsource/clangjobqueue.cpp @@ -27,6 +27,7 @@ #include "clangjobqueue.h" #include "clangdocument.h" #include "clangdocuments.h" +#include "clangtranslationunits.h" #include "projects.h" #include "unsavedfiles.h" @@ -164,43 +165,43 @@ void JobQueue::prioritizeRequests() JobRequests JobQueue::takeJobRequestsToRunNow() { JobRequests jobsToRun; - QSet documentsScheduledForThisRun; + using TranslationUnitIds = QSet; + TranslationUnitIds translationUnitsScheduledForThisRun; QMutableVectorIterator i(m_queue); while (i.hasNext()) { - const JobRequest &jobRequest = i.next(); + const JobRequest &request = i.next(); try { - const Document &document - = m_documents.document(jobRequest.filePath, - jobRequest.projectPartId); - const DocumentId documentId = DocumentId(jobRequest.filePath, jobRequest.projectPartId); + const Document &document = m_documents.document(request.filePath, + request.projectPartId); if (!document.isUsedByCurrentEditor() && !document.isVisibleInEditor()) continue; - if (documentsScheduledForThisRun.contains(documentId)) + const Utf8String id = document.translationUnit(request.preferredTranslationUnit).id(); + if (translationUnitsScheduledForThisRun.contains(id)) continue; - if (isJobRunningForDocument(documentId)) + if (isJobRunningForTranslationUnit(id)) continue; - documentsScheduledForThisRun.insert(documentId); - jobsToRun += jobRequest; + translationUnitsScheduledForThisRun.insert(id); + jobsToRun += request; i.remove(); } catch (const std::exception &exception) { qWarning() << "Error in Jobs::takeJobRequestsToRunNow for" - << jobRequest << ":" << exception.what(); + << request << ":" << exception.what(); } } return jobsToRun; } -bool JobQueue::isJobRunningForDocument(const JobQueue::DocumentId &documentId) +bool JobQueue::isJobRunningForTranslationUnit(const Utf8String &translationUnitId) { if (m_isJobRunningHandler) - return m_isJobRunningHandler(documentId.first, documentId.second); + return m_isJobRunningHandler(translationUnitId); return false; } diff --git a/src/tools/clangbackend/ipcsource/clangjobqueue.h b/src/tools/clangbackend/ipcsource/clangjobqueue.h index 1be38ceaf07..3781dda3d36 100644 --- a/src/tools/clangbackend/ipcsource/clangjobqueue.h +++ b/src/tools/clangbackend/ipcsource/clangjobqueue.h @@ -43,7 +43,7 @@ public: JobRequests processQueue(); - using IsJobRunningHandler = std::function; + using IsJobRunningHandler = std::function; void setIsJobRunningHandler(const IsJobRunningHandler &isJobRunningHandler); public: // for tests @@ -52,8 +52,7 @@ public: // for tests void prioritizeRequests(); private: - using DocumentId = QPair; - bool isJobRunningForDocument(const DocumentId &documentId); + bool isJobRunningForTranslationUnit(const Utf8String &translationUnitId); JobRequests takeJobRequestsToRunNow(); void removeOutDatedRequests(); bool isJobRequestOutDated(const JobRequest &jobRequest) const; diff --git a/src/tools/clangbackend/ipcsource/clangjobrequest.cpp b/src/tools/clangbackend/ipcsource/clangjobrequest.cpp index 4c3d692d08c..13cf7da26dc 100644 --- a/src/tools/clangbackend/ipcsource/clangjobrequest.cpp +++ b/src/tools/clangbackend/ipcsource/clangjobrequest.cpp @@ -25,6 +25,8 @@ #include "clangjobrequest.h" +#include + namespace ClangBackEnd { #define RETURN_TEXT_FOR_CASE(enumValue) case JobRequest::Type::enumValue: return #enumValue @@ -41,6 +43,18 @@ static const char *JobRequestTypeToText(JobRequest::Type type) } #undef RETURN_TEXT_FOR_CASE +#define RETURN_TEXT_FOR_CASE(enumValue) case PreferredTranslationUnit::enumValue: return #enumValue +const char *preferredTranslationUnitToText(PreferredTranslationUnit type) +{ + switch (type) { + RETURN_TEXT_FOR_CASE(RecentlyParsed); + RETURN_TEXT_FOR_CASE(PreviouslyParsed); + } + + return "UnhandledPreferredTranslationUnitType"; +} +#undef RETURN_TEXT_FOR_CASE + QDebug operator<<(QDebug debug, JobRequest::Type type) { debug << JobRequestTypeToText(type); @@ -53,12 +67,14 @@ QDebug operator<<(QDebug debug, const JobRequest &jobRequest) debug.nospace() << "Job<" << jobRequest.id << "," + << QFileInfo(jobRequest.filePath).fileName() + << "," << JobRequestTypeToText(jobRequest.type) << "," - << jobRequest.filePath + << preferredTranslationUnitToText(jobRequest.preferredTranslationUnit) << ">"; - return debug; + return debug.space(); } JobRequest::JobRequest() diff --git a/src/tools/clangbackend/ipcsource/clangjobrequest.h b/src/tools/clangbackend/ipcsource/clangjobrequest.h index 1979c51874e..f9a5604979a 100644 --- a/src/tools/clangbackend/ipcsource/clangjobrequest.h +++ b/src/tools/clangbackend/ipcsource/clangjobrequest.h @@ -25,6 +25,7 @@ #pragma once +#include "clangbackend_global.h" #include "clangclock.h" #include @@ -73,6 +74,7 @@ public: TimePoint unsavedFilesChangeTimePoint; TimePoint projectChangeTimePoint; uint documentRevision = 0; + PreferredTranslationUnit preferredTranslationUnit = PreferredTranslationUnit::RecentlyParsed; // For code completion quint32 line = 0; diff --git a/src/tools/clangbackend/ipcsource/clangjobs.cpp b/src/tools/clangbackend/ipcsource/clangjobs.cpp index c5202c15858..7626055eea6 100644 --- a/src/tools/clangbackend/ipcsource/clangjobs.cpp +++ b/src/tools/clangbackend/ipcsource/clangjobs.cpp @@ -45,9 +45,8 @@ Jobs::Jobs(Documents &documents, , m_client(client) , m_queue(documents, projectParts) { - m_queue.setIsJobRunningHandler([this](const Utf8String &filePath, - const Utf8String &projectPartId) { - return isJobRunning(filePath, projectPartId); + m_queue.setIsJobRunningHandler([this](const Utf8String &translationUnitId) { + return isJobRunning(translationUnitId); }); } @@ -97,13 +96,15 @@ bool Jobs::runJob(const JobRequest &jobRequest) JobContext context(jobRequest, &m_documents, &m_unsavedFiles, &m_client); asyncJob->setContext(context); - if (asyncJob->prepareAsyncRun()) { - qCDebug(jobsLog) << "Running" << jobRequest; + if (const IAsyncJob::AsyncPrepareResult prepareResult = asyncJob->prepareAsyncRun()) { + qCDebug(jobsLog) << "Running" << jobRequest + << "with TranslationUnit" << prepareResult.translationUnitId; asyncJob->setFinishedHandler([this](IAsyncJob *asyncJob){ onJobFinished(asyncJob); }); const QFuture future = asyncJob->runAsync(); - m_running.insert(asyncJob, RunningJob{jobRequest, future}); + const RunningJob runningJob{jobRequest, prepareResult.translationUnitId, future}; + m_running.insert(asyncJob, runningJob); return true; } else { qCDebug(jobsLog) << "Preparation failed for " << jobRequest; @@ -134,12 +135,10 @@ JobRequests Jobs::queue() const return m_queue.queue(); } -bool Jobs::isJobRunning(const Utf8String &filePath, const Utf8String &projectPartId) const +bool Jobs::isJobRunning(const Utf8String &translationUnitId) const { - const auto hasJobRequest = [filePath, projectPartId](const RunningJob &runningJob) { - const JobRequest &jobRequest = runningJob.jobRequest; - return filePath == jobRequest.filePath - && projectPartId == jobRequest.projectPartId; + const auto hasJobRequest = [translationUnitId](const RunningJob &runningJob) { + return runningJob.translationUnitId == translationUnitId; }; return Utils::anyOf(m_running.values(), hasJobRequest); diff --git a/src/tools/clangbackend/ipcsource/clangjobs.h b/src/tools/clangbackend/ipcsource/clangjobs.h index 85d47488580..21c3576a48f 100644 --- a/src/tools/clangbackend/ipcsource/clangjobs.h +++ b/src/tools/clangbackend/ipcsource/clangjobs.h @@ -43,6 +43,7 @@ class Jobs public: struct RunningJob { JobRequest jobRequest; + Utf8String translationUnitId; QFuture future; }; using RunningJobs = QHash; @@ -61,7 +62,7 @@ public: public /*for tests*/: QList runningJobs() const; JobRequests queue() const; - bool isJobRunning(const Utf8String &filePath, const Utf8String &projectPartId) const; + bool isJobRunning(const Utf8String &translationUnitId) const; private: JobRequests runJobs(const JobRequests &jobRequest); diff --git a/src/tools/clangbackend/ipcsource/clangrequestdocumentannotationsjob.cpp b/src/tools/clangbackend/ipcsource/clangrequestdocumentannotationsjob.cpp index 689fe2b9f19..e53ff0b8007 100644 --- a/src/tools/clangbackend/ipcsource/clangrequestdocumentannotationsjob.cpp +++ b/src/tools/clangbackend/ipcsource/clangrequestdocumentannotationsjob.cpp @@ -47,10 +47,11 @@ static RequestDocumentAnnotationsJob::AsyncResult runAsyncHelper( return asyncResult; } -bool RequestDocumentAnnotationsJob::prepareAsyncRun() +IAsyncJob::AsyncPrepareResult RequestDocumentAnnotationsJob::prepareAsyncRun() { const JobRequest jobRequest = context().jobRequest; - QTC_ASSERT(jobRequest.type == JobRequest::Type::RequestDocumentAnnotations, return false); + QTC_ASSERT(jobRequest.type == JobRequest::Type::RequestDocumentAnnotations, + return AsyncPrepareResult()); try { m_pinnedDocument = context().documentForJobRequest(); @@ -60,13 +61,12 @@ bool RequestDocumentAnnotationsJob::prepareAsyncRun() setRunner([translationUnit]() { return runAsyncHelper(translationUnit); }); + return AsyncPrepareResult{translationUnit.id()}; } catch (const std::exception &exception) { qWarning() << "Error in RequestDocumentAnnotationsJob::prepareAsyncRun:" << exception.what(); - return false; + return AsyncPrepareResult(); } - - return true; } void RequestDocumentAnnotationsJob::finalizeAsyncRun() diff --git a/src/tools/clangbackend/ipcsource/clangrequestdocumentannotationsjob.h b/src/tools/clangbackend/ipcsource/clangrequestdocumentannotationsjob.h index 04bdd7cfc3a..b9ff1c822c7 100644 --- a/src/tools/clangbackend/ipcsource/clangrequestdocumentannotationsjob.h +++ b/src/tools/clangbackend/ipcsource/clangrequestdocumentannotationsjob.h @@ -46,7 +46,7 @@ class RequestDocumentAnnotationsJob : public AsyncJob #include +#include +#include #include #include #include @@ -60,7 +62,9 @@ protected: Utf8String createTranslationUnitForDeletedFile(); JobRequest createJobRequest(const Utf8String &filePath, - JobRequest::Type type) const; + JobRequest::Type type, + PreferredTranslationUnit preferredTranslationUnit + = PreferredTranslationUnit::RecentlyParsed) const; void updateDocumentRevision(); void updateUnsavedFiles(); @@ -240,7 +244,7 @@ TEST_F(JobQueue, RunNothingForNotCurrentOrVisibleDocument) ASSERT_THAT(jobsToRun.size(), Eq(0)); } -TEST_F(JobQueue, RunOnlyOneJobPerDocumentIfMultipleAreInQueue) +TEST_F(JobQueue, RunOnlyOneJobPerTranslationUnitIfMultipleAreInQueue) { jobQueue.add(createJobRequest(filePath1, JobRequest::Type::UpdateDocumentAnnotations)); jobQueue.add(createJobRequest(filePath1, JobRequest::Type::UpdateDocumentAnnotations)); @@ -251,12 +255,30 @@ TEST_F(JobQueue, RunOnlyOneJobPerDocumentIfMultipleAreInQueue) ASSERT_THAT(jobQueue.size(), Eq(1)); } -TEST_F(JobQueue, DoNotRunJobForDocumentThatIsBeingProcessed) +TEST_F(JobQueue, RunJobsForDistinctTranslationUnits) +{ + const TranslationUnit initialTu = document.translationUnit(); + document.translationUnits().updateParseTimePoint(initialTu.id(), std::chrono::steady_clock::now()); + const TranslationUnit alternativeTu = document.translationUnits().createAndAppend(); + document.translationUnits().updateParseTimePoint(alternativeTu.id(), std::chrono::steady_clock::now()); + jobQueue.add(createJobRequest(filePath1, + JobRequest::Type::UpdateDocumentAnnotations, + PreferredTranslationUnit::RecentlyParsed)); + jobQueue.add(createJobRequest(filePath1, + JobRequest::Type::UpdateDocumentAnnotations, + PreferredTranslationUnit::PreviouslyParsed)); + + const JobRequests jobsToRun = jobQueue.processQueue(); + + ASSERT_THAT(jobsToRun.size(), Eq(2)); + ASSERT_THAT(jobQueue.size(), Eq(0)); +} +TEST_F(JobQueue, DoNotRunJobForTranslationUnittThatIsBeingProcessed) { jobQueue.add(createJobRequest(filePath1, JobRequest::Type::UpdateDocumentAnnotations)); jobQueue.add(createJobRequest(filePath1, JobRequest::Type::UpdateDocumentAnnotations)); JobRequests jobsToRun = jobQueue.processQueue(); - jobQueue.setIsJobRunningHandler([](const Utf8String &, const Utf8String &) { + jobQueue.setIsJobRunningHandler([](const Utf8String &) { return true; }); @@ -397,8 +419,10 @@ Utf8String JobQueue::createTranslationUnitForDeletedFile() return temporaryFilePath; } -JobRequest JobQueue::createJobRequest(const Utf8String &filePath, - JobRequest::Type type) const +JobRequest JobQueue::createJobRequest( + const Utf8String &filePath, + JobRequest::Type type, + PreferredTranslationUnit preferredTranslationUnit) const { JobRequest jobRequest; jobRequest.type = type; @@ -407,6 +431,7 @@ JobRequest JobQueue::createJobRequest(const Utf8String &filePath, jobRequest.projectPartId = projectPartId; jobRequest.unsavedFilesChangeTimePoint = unsavedFiles.lastChangeTimePoint(); jobRequest.documentRevision = document.documentRevision(); + jobRequest.preferredTranslationUnit = preferredTranslationUnit; jobRequest.projectChangeTimePoint = projects.project(projectPartId).lastChangeTimePoint(); return jobRequest; diff --git a/tests/unit/unittest/clangjobs-test.cpp b/tests/unit/unittest/clangjobs-test.cpp index d5ef57155b5..fffc6f82375 100644 --- a/tests/unit/unittest/clangjobs-test.cpp +++ b/tests/unit/unittest/clangjobs-test.cpp @@ -108,7 +108,7 @@ TEST_F(Jobs, IsJobRunning) jobs.add(createJobRequest(filePath1, JobRequest::Type::UpdateDocumentAnnotations)); jobs.process(); - const bool isJobRunning = jobs.isJobRunning(filePath1, projectPartId); + const bool isJobRunning = jobs.isJobRunning(document.translationUnit().id()); ASSERT_TRUE(isJobRunning); } From 6ede503aa385fb6948b8700e352ae3b29eebda6b Mon Sep 17 00:00:00 2001 From: Nikolai Kosjar Date: Thu, 15 Sep 2016 12:34:15 +0200 Subject: [PATCH 09/27] Clang: Soft assert creation of a job Change-Id: I59af185fe2942eb4cd191692c56e6449610312e3 Reviewed-by: David Schulz --- .../clangbackend/ipcsource/clangjobs.cpp | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/tools/clangbackend/ipcsource/clangjobs.cpp b/src/tools/clangbackend/ipcsource/clangjobs.cpp index 7626055eea6..bd5a982d014 100644 --- a/src/tools/clangbackend/ipcsource/clangjobs.cpp +++ b/src/tools/clangbackend/ipcsource/clangjobs.cpp @@ -92,24 +92,25 @@ JobRequests Jobs::runJobs(const JobRequests &jobsRequests) bool Jobs::runJob(const JobRequest &jobRequest) { - if (IAsyncJob *asyncJob = IAsyncJob::create(jobRequest.type)) { - JobContext context(jobRequest, &m_documents, &m_unsavedFiles, &m_client); - asyncJob->setContext(context); + IAsyncJob *asyncJob = IAsyncJob::create(jobRequest.type); + QTC_ASSERT(asyncJob, return false); - if (const IAsyncJob::AsyncPrepareResult prepareResult = asyncJob->prepareAsyncRun()) { - qCDebug(jobsLog) << "Running" << jobRequest - << "with TranslationUnit" << prepareResult.translationUnitId; + JobContext context(jobRequest, &m_documents, &m_unsavedFiles, &m_client); + asyncJob->setContext(context); - asyncJob->setFinishedHandler([this](IAsyncJob *asyncJob){ onJobFinished(asyncJob); }); - const QFuture future = asyncJob->runAsync(); + if (const IAsyncJob::AsyncPrepareResult prepareResult = asyncJob->prepareAsyncRun()) { + qCDebug(jobsLog) << "Running" << jobRequest + << "with TranslationUnit" << prepareResult.translationUnitId; - const RunningJob runningJob{jobRequest, prepareResult.translationUnitId, future}; - m_running.insert(asyncJob, runningJob); - return true; - } else { - qCDebug(jobsLog) << "Preparation failed for " << jobRequest; - delete asyncJob; - } + asyncJob->setFinishedHandler([this](IAsyncJob *asyncJob){ onJobFinished(asyncJob); }); + const QFuture future = asyncJob->runAsync(); + + const RunningJob runningJob{jobRequest, prepareResult.translationUnitId, future}; + m_running.insert(asyncJob, runningJob); + return true; + } else { + qCDebug(jobsLog) << "Preparation failed for " << jobRequest; + delete asyncJob; } return false; From a85d5c7222dc131b885c7eaeb0a14f8d25299f2f Mon Sep 17 00:00:00 2001 From: Nikolai Kosjar Date: Thu, 15 Sep 2016 14:38:42 +0200 Subject: [PATCH 10/27] Clang: Return updated documents Change-Id: Id4b1267914019ac56cdd132c6d597167a1f2b9a9 Reviewed-by: David Schulz --- .../clangbackend/ipcsource/clangdocuments.cpp | 14 +++++++++++--- src/tools/clangbackend/ipcsource/clangdocuments.h | 4 ++-- tests/unit/unittest/clangdocuments-test.cpp | 13 +++++++++++++ 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/tools/clangbackend/ipcsource/clangdocuments.cpp b/src/tools/clangbackend/ipcsource/clangdocuments.cpp index d85a0de49fd..6077505b573 100644 --- a/src/tools/clangbackend/ipcsource/clangdocuments.cpp +++ b/src/tools/clangbackend/ipcsource/clangdocuments.cpp @@ -68,14 +68,20 @@ std::vector Documents::create(const QVector &fileContai return createdDocuments; } -void Documents::update(const QVector &fileContainers) +std::vector Documents::update(const QVector &fileContainers) { checkIfDocumentsForFilePathsExist(fileContainers); + std::vector createdDocuments; + for (const FileContainer &fileContainer : fileContainers) { - updateDocument(fileContainer); + const std::vector documents = updateDocument(fileContainer); + createdDocuments.insert(createdDocuments.end(), documents.begin(), documents.end()); + updateDocumentsWithChangedDependency(fileContainer.filePath()); } + + return createdDocuments; } static bool removeFromFileContainer(QVector &fileContainers, const Document &document) @@ -205,12 +211,14 @@ Document Documents::createDocument(const FileContainer &fileContainer) return documents_.back(); } -void Documents::updateDocument(const FileContainer &fileContainer) +std::vector Documents::updateDocument(const FileContainer &fileContainer) { const auto documents = findAllDocumentsWithFilePath(fileContainer.filePath()); for (auto document : documents) document.setDocumentRevision(fileContainer.documentRevision()); + + return documents; } std::vector::iterator Documents::findDocument(const FileContainer &fileContainer) diff --git a/src/tools/clangbackend/ipcsource/clangdocuments.h b/src/tools/clangbackend/ipcsource/clangdocuments.h index 3af689145bd..90c905f16c4 100644 --- a/src/tools/clangbackend/ipcsource/clangdocuments.h +++ b/src/tools/clangbackend/ipcsource/clangdocuments.h @@ -45,7 +45,7 @@ public: Documents(ProjectParts &projectParts, UnsavedFiles &unsavedFiles); std::vector create(const QVector &fileContainers); - void update(const QVector &fileContainers); + std::vector update(const QVector &fileContainers); void remove(const QVector &fileContainers); void setUsedByCurrentEditor(const Utf8String &filePath); @@ -72,7 +72,7 @@ public: private: Document createDocument(const FileContainer &fileContainer); - void updateDocument(const FileContainer &fileContainer); + std::vector updateDocument(const FileContainer &fileContainer); std::vector::iterator findDocument(const FileContainer &fileContainer); std::vector findAllDocumentsWithFilePath(const Utf8String &filePath); std::vector::const_iterator findDocument(const Utf8String &filePath, const Utf8String &projectPartId) const; diff --git a/tests/unit/unittest/clangdocuments-test.cpp b/tests/unit/unittest/clangdocuments-test.cpp index 0be8c6281a2..58159924413 100644 --- a/tests/unit/unittest/clangdocuments-test.cpp +++ b/tests/unit/unittest/clangdocuments-test.cpp @@ -43,6 +43,7 @@ using ClangBackEnd::ProjectPartContainer; using testing::IsNull; using testing::NotNull; using testing::Gt; +using testing::Eq; using testing::Not; using testing::Contains; @@ -158,6 +159,18 @@ TEST_F(Documents, UpdateSingle) IsDocument(filePath, projectPartId, 75u)); } +TEST_F(Documents, UpdateReturnsUpdatedDocument) +{ + ClangBackEnd::FileContainer createFileContainer(filePath, projectPartId, Utf8StringVector(), 74u); + ClangBackEnd::FileContainer updateFileContainer(filePath, Utf8String(), Utf8StringVector(), 75u); + documents.create({createFileContainer}); + + const std::vector updatedDocuments = documents.update({updateFileContainer}); + + ASSERT_THAT(updatedDocuments.size(), Eq(1u)); + ASSERT_THAT(updatedDocuments.front().documentRevision(), Eq(75u)); +} + TEST_F(Documents, UpdateMultiple) { ClangBackEnd::FileContainer fileContainer(filePath, projectPartId, Utf8StringVector(), 74u); From 380d756a03346e5bbb83101f49ed6f6696c42c6a Mon Sep 17 00:00:00 2001 From: Nikolai Kosjar Date: Wed, 14 Sep 2016 16:16:10 +0200 Subject: [PATCH 11/27] Clang: Hook up supportive translation unit on first edit Parsing happens rotationally on the translation units. The recently parsed translation unit is used for completion jobs while the older version is used for parse jobs. Advantages: A1. A completion job cannot be blocked anymore by currently running parse job. A2. Faster triggering of parse jobs. A reparse was triggered about 1650ms after the last keystroke. This is down to 500ms now since we do not have a blocking translation unit for the completion anymore. Disadvantages: D1. Memory consumption is doubled for an edited document. This could be addressed by suspending the second translation unit after some time of inactivity. D2. Setup of the supportive translation unit takes some time. Change-Id: I958c883c01f274530f5482c788c15cd38d6f4c3e Reviewed-by: David Schulz --- .../clangeditordocumentprocessor.cpp | 14 +- .../clangeditordocumentprocessor.h | 4 + src/plugins/cppeditor/cppeditordocument.cpp | 2 + .../cpptools/baseeditordocumentprocessor.cpp | 4 + .../cpptools/baseeditordocumentprocessor.h | 2 + .../ipcsource/clangbackend_global.h | 1 + .../ipcsource/clangbackendclangipc-source.pri | 8 +- .../ipcsource/clangcodemodelserver.cpp | 43 ++- .../ipcsource/clangcodemodelserver.h | 8 +- .../ipcsource/clangcompletecodejob.cpp | 3 +- .../clangcreateinitialdocumentpreamblejob.cpp | 3 +- .../ipcsource/clangdocumentprocessor.cpp | 41 ++- .../ipcsource/clangdocumentprocessor.h | 6 + .../clangbackend/ipcsource/clangiasyncjob.cpp | 6 + .../ipcsource/clangjobrequest.cpp | 5 + .../clangbackend/ipcsource/clangjobrequest.h | 11 + .../clangbackend/ipcsource/clangjobs.cpp | 10 + src/tools/clangbackend/ipcsource/clangjobs.h | 7 + ...clangparsesupportivetranslationunitjob.cpp | 78 ++++++ .../clangparsesupportivetranslationunitjob.h | 51 ++++ ...angreparsesupportivetranslationunitjob.cpp | 82 ++++++ ...clangreparsesupportivetranslationunitjob.h | 51 ++++ .../clangrequestdocumentannotationsjob.cpp | 3 +- ...ngsupportivetranslationunitinitializer.cpp | 141 ++++++++++ ...langsupportivetranslationunitinitializer.h | 77 +++++ .../ipcsource/clangtranslationunits.cpp | 19 +- .../ipcsource/clangtranslationunits.h | 7 +- .../clangupdatedocumentannotationsjob.cpp | 3 +- tests/unit/unittest/clangipcserver-test.cpp | 57 ++++ ...gparsesupportivetranslationunitjobtest.cpp | 86 ++++++ ...eparsesupportivetranslationunitjobtest.cpp | 109 ++++++++ ...pportivetranslationunitinitializertest.cpp | 262 ++++++++++++++++++ .../unittest/clangtranslationunits-test.cpp | 11 + tests/unit/unittest/unittest.pro | 3 + 34 files changed, 1201 insertions(+), 17 deletions(-) create mode 100644 src/tools/clangbackend/ipcsource/clangparsesupportivetranslationunitjob.cpp create mode 100644 src/tools/clangbackend/ipcsource/clangparsesupportivetranslationunitjob.h create mode 100644 src/tools/clangbackend/ipcsource/clangreparsesupportivetranslationunitjob.cpp create mode 100644 src/tools/clangbackend/ipcsource/clangreparsesupportivetranslationunitjob.h create mode 100644 src/tools/clangbackend/ipcsource/clangsupportivetranslationunitinitializer.cpp create mode 100644 src/tools/clangbackend/ipcsource/clangsupportivetranslationunitinitializer.h create mode 100644 tests/unit/unittest/clangparsesupportivetranslationunitjobtest.cpp create mode 100644 tests/unit/unittest/clangreparsesupportivetranslationunitjobtest.cpp create mode 100644 tests/unit/unittest/clangsupportivetranslationunitinitializertest.cpp diff --git a/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp b/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp index 8a4919e3786..99c6a670339 100644 --- a/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp +++ b/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp @@ -72,6 +72,11 @@ ClangEditorDocumentProcessor::ClangEditorDocumentProcessor( , m_semanticHighlighter(document) , m_builtinProcessor(document, /*enableSemanticHighlighter=*/ false) { + m_updateTranslationUnitTimer.setSingleShot(true); + m_updateTranslationUnitTimer.setInterval(350); + connect(&m_updateTranslationUnitTimer, &QTimer::timeout, + this, &ClangEditorDocumentProcessor::updateTranslationUnitIfProjectPartExists); + // Forwarding the semantic info from the builtin processor enables us to provide all // editor (widget) related features that are not yet implemented by the clang plugin. connect(&m_builtinProcessor, &CppTools::BuiltinEditorDocumentProcessor::cppDocumentUpdated, @@ -82,6 +87,8 @@ ClangEditorDocumentProcessor::ClangEditorDocumentProcessor( ClangEditorDocumentProcessor::~ClangEditorDocumentProcessor() { + m_updateTranslationUnitTimer.stop(); + m_parserWatcher.cancel(); m_parserWatcher.waitForFinished(); @@ -93,7 +100,7 @@ ClangEditorDocumentProcessor::~ClangEditorDocumentProcessor() void ClangEditorDocumentProcessor::run() { - updateTranslationUnitIfProjectPartExists(); + m_updateTranslationUnitTimer.start(); // Run clang parser disconnect(&m_parserWatcher, &QFutureWatcher::finished, @@ -251,6 +258,11 @@ void ClangEditorDocumentProcessor::addDiagnosticToolTipToLayout(uint line, addToolTipToLayout(diagnostic, target); } +void ClangEditorDocumentProcessor::editorDocumentTimerRestarted() +{ + m_updateTranslationUnitTimer.stop(); // Wait for the next call to run(). +} + ClangBackEnd::FileContainer ClangEditorDocumentProcessor::fileContainerWithArguments() const { return fileContainerWithArguments(m_projectPart.data()); diff --git a/src/plugins/clangcodemodel/clangeditordocumentprocessor.h b/src/plugins/clangcodemodel/clangeditordocumentprocessor.h index e9ec8ebd185..a23d93d72fb 100644 --- a/src/plugins/clangcodemodel/clangeditordocumentprocessor.h +++ b/src/plugins/clangcodemodel/clangeditordocumentprocessor.h @@ -32,6 +32,7 @@ #include #include +#include namespace ClangBackEnd { class DiagnosticContainer; @@ -78,6 +79,8 @@ public: bool hasDiagnosticsAt(uint line, uint column) const override; void addDiagnosticToolTipToLayout(uint line, uint column, QLayout *target) const override; + void editorDocumentTimerRestarted() override; + ClangBackEnd::FileContainer fileContainerWithArguments() const; void clearDiagnosticsWithFixIts(); @@ -102,6 +105,7 @@ private: QSharedPointer m_parser; CppTools::ProjectPart::Ptr m_projectPart; QFutureWatcher m_parserWatcher; + QTimer m_updateTranslationUnitTimer; unsigned m_parserRevision; CppTools::SemanticHighlighter m_semanticHighlighter; diff --git a/src/plugins/cppeditor/cppeditordocument.cpp b/src/plugins/cppeditor/cppeditordocument.cpp index 00ff24aca9b..91918f9d3bd 100644 --- a/src/plugins/cppeditor/cppeditordocument.cpp +++ b/src/plugins/cppeditor/cppeditordocument.cpp @@ -230,12 +230,14 @@ void CppEditorDocument::scheduleProcessDocument() { m_processorRevision = document()->revision(); m_processorTimer.start(); + processor()->editorDocumentTimerRestarted(); } void CppEditorDocument::processDocument() { if (processor()->isParserRunning() || m_processorRevision != contentsRevision()) { m_processorTimer.start(); + processor()->editorDocumentTimerRestarted(); return; } diff --git a/src/plugins/cpptools/baseeditordocumentprocessor.cpp b/src/plugins/cpptools/baseeditordocumentprocessor.cpp index 82aa058b90d..1ae7f11b98e 100644 --- a/src/plugins/cpptools/baseeditordocumentprocessor.cpp +++ b/src/plugins/cpptools/baseeditordocumentprocessor.cpp @@ -67,6 +67,10 @@ void BaseEditorDocumentProcessor::addDiagnosticToolTipToLayout(uint, uint, QLayo { } +void BaseEditorDocumentProcessor::editorDocumentTimerRestarted() +{ +} + void BaseEditorDocumentProcessor::runParser(QFutureInterface &future, BaseEditorDocumentParser::Ptr parser, const WorkingCopy workingCopy) diff --git a/src/plugins/cpptools/baseeditordocumentprocessor.h b/src/plugins/cpptools/baseeditordocumentprocessor.h index 77723fcd4ce..da6a20ca943 100644 --- a/src/plugins/cpptools/baseeditordocumentprocessor.h +++ b/src/plugins/cpptools/baseeditordocumentprocessor.h @@ -67,6 +67,8 @@ public: virtual bool hasDiagnosticsAt(uint line, uint column) const; virtual void addDiagnosticToolTipToLayout(uint line, uint column, QLayout *layout) const; + virtual void editorDocumentTimerRestarted(); + signals: // Signal interface to implement void codeWarningsUpdated(unsigned revision, diff --git a/src/tools/clangbackend/ipcsource/clangbackend_global.h b/src/tools/clangbackend/ipcsource/clangbackend_global.h index ff17d1c1046..0dccaa87ad4 100644 --- a/src/tools/clangbackend/ipcsource/clangbackend_global.h +++ b/src/tools/clangbackend/ipcsource/clangbackend_global.h @@ -31,6 +31,7 @@ enum class PreferredTranslationUnit { RecentlyParsed, PreviouslyParsed, + LastUninitialized, }; } // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri b/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri index 5fbe996d06b..cf0a0e6682e 100644 --- a/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri +++ b/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri @@ -47,6 +47,9 @@ HEADERS += $$PWD/clangcodemodelserver.h \ $$PWD/clangdocumentprocessors.h \ $$PWD/clangtranslationunits.h \ $$PWD/clangclock.h \ + $$PWD/clangsupportivetranslationunitinitializer.h \ + $$PWD/clangparsesupportivetranslationunitjob.h \ + $$PWD/clangreparsesupportivetranslationunitjob.h \ SOURCES += $$PWD/clangcodemodelserver.cpp \ $$PWD/codecompleter.cpp \ @@ -88,4 +91,7 @@ SOURCES += $$PWD/clangcodemodelserver.cpp \ $$PWD/clangexceptions.cpp \ $$PWD/clangdocumentprocessor.cpp \ $$PWD/clangdocumentprocessors.cpp \ - $$PWD/clangtranslationunits.cpp + $$PWD/clangtranslationunits.cpp \ + $$PWD/clangsupportivetranslationunitinitializer.cpp \ + $$PWD/clangparsesupportivetranslationunitjob.cpp \ + $$PWD/clangreparsesupportivetranslationunitjob.cpp \ diff --git a/src/tools/clangbackend/ipcsource/clangcodemodelserver.cpp b/src/tools/clangbackend/ipcsource/clangcodemodelserver.cpp index 1b26c39fadf..bea5eacea49 100644 --- a/src/tools/clangbackend/ipcsource/clangcodemodelserver.cpp +++ b/src/tools/clangbackend/ipcsource/clangcodemodelserver.cpp @@ -27,6 +27,7 @@ #include "clangdocuments.h" #include "clangfilesystemwatcher.h" +#include "clangtranslationunits.h" #include "codecompleter.h" #include "diagnosticset.h" #include "highlightingmarks.h" @@ -126,10 +127,16 @@ void ClangCodeModelServer::updateTranslationUnitsForEditor(const UpdateTranslati try { const auto newerFileContainers = documents.newerFileContainers(message.fileContainers()); if (newerFileContainers.size() > 0) { - documents.update(newerFileContainers); + const std::vector updateDocuments = documents.update(newerFileContainers); unsavedFiles.createOrUpdate(newerFileContainers); - updateDocumentAnnotationsTimer.start(updateDocumentAnnotationsTimeOutInMs); + // Start the jobs on the next event loop iteration since otherwise + // we might block the translation unit for a completion request + // that comes right after this message. + updateDocumentAnnotationsTimer.start(0); + QTimer::singleShot(0, [this, updateDocuments](){ + startInitializingSupportiveTranslationUnits(updateDocuments); + }); } } catch (const std::exception &exception) { qWarning() << "Error in ClangCodeModelServer::updateTranslationUnitsForEditor:" << exception.what(); @@ -286,7 +293,9 @@ void ClangCodeModelServer::processJobsForDirtyAndVisibleDocuments() for (const auto &document : documents.documents()) { if (document.isNeedingReparse() && document.isVisibleInEditor()) { DocumentProcessor processor = documentProcessors().processor(document); - processor.addJob(createJobRequest(document, JobRequest::Type::UpdateDocumentAnnotations)); + processor.addJob(createJobRequest(document, + JobRequest::Type::UpdateDocumentAnnotations, + PreferredTranslationUnit::PreviouslyParsed)); } } @@ -297,14 +306,37 @@ void ClangCodeModelServer::processInitialJobsForDocuments(const std::vector &documents) +{ + for (const Document &document : documents) { + try { + DocumentProcessor processor = documentProcessors().processor(document); + if (!processor.hasSupportiveTranslationUnit()) + processor.startInitializingSupportiveTranslationUnit(); + } catch (const DocumentProcessorDoesNotExist &) { + // OK, document was already closed. + } + } +} + +JobRequest ClangCodeModelServer::createJobRequest( + const Document &document, + JobRequest::Type type, + PreferredTranslationUnit preferredTranslationUnit) const { JobRequest jobRequest; jobRequest.type = type; @@ -313,6 +345,7 @@ JobRequest ClangCodeModelServer::createJobRequest(const Document &document, jobRequest.projectPartId = document.projectPartId(); jobRequest.unsavedFilesChangeTimePoint = unsavedFiles.lastChangeTimePoint(); jobRequest.documentRevision = document.documentRevision(); + jobRequest.preferredTranslationUnit = preferredTranslationUnit; const ProjectPart &projectPart = projects.project(document.projectPartId()); jobRequest.projectChangeTimePoint = projectPart.lastChangeTimePoint(); diff --git a/src/tools/clangbackend/ipcsource/clangcodemodelserver.h b/src/tools/clangbackend/ipcsource/clangcodemodelserver.h index efebb054da9..ab4efdb7e29 100644 --- a/src/tools/clangbackend/ipcsource/clangcodemodelserver.h +++ b/src/tools/clangbackend/ipcsource/clangcodemodelserver.h @@ -65,16 +65,20 @@ public: // for tests int queueSizeForTestsOnly(); bool isTimerRunningForTestOnly() const; void setUpdateDocumentAnnotationsTimeOutInMsForTestsOnly(int value); + DocumentProcessors &documentProcessors(); private: - DocumentProcessors &documentProcessors(); void startDocumentAnnotationsTimerIfFileIsNotOpenAsDocument(const Utf8String &filePath); void addJobRequestsForDirtyAndVisibleDocuments(); void processJobsForDirtyAndVisibleDocuments(); void processInitialJobsForDocuments(const std::vector &documents); + void startInitializingSupportiveTranslationUnits(const std::vector &documents); - JobRequest createJobRequest(const Document &document, JobRequest::Type type) const; + JobRequest createJobRequest(const Document &document, + JobRequest::Type type, + PreferredTranslationUnit preferredTranslationUnit + = PreferredTranslationUnit::RecentlyParsed) const; private: ProjectParts projects; diff --git a/src/tools/clangbackend/ipcsource/clangcompletecodejob.cpp b/src/tools/clangbackend/ipcsource/clangcompletecodejob.cpp index 8d13869bb8a..d45f53fdda0 100644 --- a/src/tools/clangbackend/ipcsource/clangcompletecodejob.cpp +++ b/src/tools/clangbackend/ipcsource/clangcompletecodejob.cpp @@ -58,7 +58,8 @@ IAsyncJob::AsyncPrepareResult CompleteCodeJob::prepareAsyncRun() try { m_pinnedDocument = context().documentForJobRequest(); - const TranslationUnit translationUnit = m_pinnedDocument.translationUnit(); + const TranslationUnit translationUnit + = m_pinnedDocument.translationUnit(jobRequest.preferredTranslationUnit); const UnsavedFiles unsavedFiles = *context().unsavedFiles; const quint32 line = jobRequest.line; const quint32 column = jobRequest.column; diff --git a/src/tools/clangbackend/ipcsource/clangcreateinitialdocumentpreamblejob.cpp b/src/tools/clangbackend/ipcsource/clangcreateinitialdocumentpreamblejob.cpp index f24a3978e9e..a9bf6698661 100644 --- a/src/tools/clangbackend/ipcsource/clangcreateinitialdocumentpreamblejob.cpp +++ b/src/tools/clangbackend/ipcsource/clangcreateinitialdocumentpreamblejob.cpp @@ -48,7 +48,8 @@ IAsyncJob::AsyncPrepareResult CreateInitialDocumentPreambleJob::prepareAsyncRun( m_pinnedDocument = context().documentForJobRequest(); m_pinnedFileContainer = m_pinnedDocument.fileContainer(); - const TranslationUnit translationUnit = m_pinnedDocument.translationUnit(); + const TranslationUnit translationUnit + = m_pinnedDocument.translationUnit(jobRequest.preferredTranslationUnit); const TranslationUnitUpdateInput updateInput = m_pinnedDocument.createUpdateInput(); setRunner([translationUnit, updateInput]() { return runAsyncHelper(translationUnit, updateInput); diff --git a/src/tools/clangbackend/ipcsource/clangdocumentprocessor.cpp b/src/tools/clangbackend/ipcsource/clangdocumentprocessor.cpp index 0b4faf9251f..b0a0e0122f1 100644 --- a/src/tools/clangbackend/ipcsource/clangdocumentprocessor.cpp +++ b/src/tools/clangbackend/ipcsource/clangdocumentprocessor.cpp @@ -25,9 +25,14 @@ #include "clangdocumentprocessor.h" +#include "clangdocuments.h" #include "clangjobs.h" +#include "clangsupportivetranslationunitinitializer.h" #include "clangdocument.h" +#include "clangtranslationunits.h" + +#include namespace ClangBackEnd { @@ -40,12 +45,24 @@ public: ProjectParts &projects, ClangCodeModelClientInterface &client) : document(document) + , documents(documents) , jobs(documents, unsavedFiles, projects, client) - {} + , supportiveTranslationUnitInitializer(document, jobs) + { + const auto isDocumentClosedChecker = [this](const Utf8String &filePath, + const Utf8String &projectPartId) { + return !this->documents.hasDocument(filePath, projectPartId); + }; + supportiveTranslationUnitInitializer.setIsDocumentClosedChecker(isDocumentClosedChecker); + } public: Document document; + Documents &documents; Jobs jobs; + + SupportiveTranslationUnitInitializer supportiveTranslationUnitInitializer; + JobRequestCreator jobRequestCreator; }; DocumentProcessor::DocumentProcessor(const Document &document, @@ -61,6 +78,11 @@ DocumentProcessor::DocumentProcessor(const Document &document, { } +void DocumentProcessor::setJobRequestCreator(const JobRequestCreator &creator) +{ + d->supportiveTranslationUnitInitializer.setJobRequestCreator(creator); +} + void DocumentProcessor::addJob(const JobRequest &jobRequest) { d->jobs.add(jobRequest); @@ -76,6 +98,23 @@ Document DocumentProcessor::document() const return d->document; } +bool DocumentProcessor::hasSupportiveTranslationUnit() const +{ + return d->supportiveTranslationUnitInitializer.state() + != SupportiveTranslationUnitInitializer::State::NotInitialized; +} + +void DocumentProcessor::startInitializingSupportiveTranslationUnit() +{ + d->supportiveTranslationUnitInitializer.startInitializing(); +} + +bool DocumentProcessor::isSupportiveTranslationUnitInitialized() const +{ + return d->supportiveTranslationUnitInitializer.state() + == SupportiveTranslationUnitInitializer::State::Initialized; +} + QList DocumentProcessor::runningJobs() const { return d->jobs.runningJobs(); diff --git a/src/tools/clangbackend/ipcsource/clangdocumentprocessor.h b/src/tools/clangbackend/ipcsource/clangdocumentprocessor.h index 0ad452b314a..364d1fecaea 100644 --- a/src/tools/clangbackend/ipcsource/clangdocumentprocessor.h +++ b/src/tools/clangbackend/ipcsource/clangdocumentprocessor.h @@ -49,12 +49,18 @@ public: ProjectParts &projects, ClangCodeModelClientInterface &client); + void setJobRequestCreator(const JobRequestCreator &creator); + void addJob(const JobRequest &jobRequest); JobRequests process(); Document document() const; + bool hasSupportiveTranslationUnit() const; + void startInitializingSupportiveTranslationUnit(); + public: // for tests + bool isSupportiveTranslationUnitInitialized() const; QList runningJobs() const; int queueSize() const; diff --git a/src/tools/clangbackend/ipcsource/clangiasyncjob.cpp b/src/tools/clangbackend/ipcsource/clangiasyncjob.cpp index 8c433c40928..d6fc0ff659f 100644 --- a/src/tools/clangbackend/ipcsource/clangiasyncjob.cpp +++ b/src/tools/clangbackend/ipcsource/clangiasyncjob.cpp @@ -27,6 +27,8 @@ #include "clangcompletecodejob.h" #include "clangcreateinitialdocumentpreamblejob.h" +#include "clangparsesupportivetranslationunitjob.h" +#include "clangreparsesupportivetranslationunitjob.h" #include "clangrequestdocumentannotationsjob.h" #include "clangupdatedocumentannotationsjob.h" @@ -39,6 +41,10 @@ IAsyncJob *IAsyncJob::create(JobRequest::Type type) switch (type) { case JobRequest::Type::UpdateDocumentAnnotations: return new UpdateDocumentAnnotationsJob(); + case JobRequest::Type::ParseSupportiveTranslationUnit: + return new ParseSupportiveTranslationUnitJob(); + case JobRequest::Type::ReparseSupportiveTranslationUnit: + return new ReparseSupportiveTranslationUnitJob(); case JobRequest::Type::CreateInitialDocumentPreamble: return new CreateInitialDocumentPreambleJob(); case JobRequest::Type::CompleteCode: diff --git a/src/tools/clangbackend/ipcsource/clangjobrequest.cpp b/src/tools/clangbackend/ipcsource/clangjobrequest.cpp index 13cf7da26dc..73fe6f4e68b 100644 --- a/src/tools/clangbackend/ipcsource/clangjobrequest.cpp +++ b/src/tools/clangbackend/ipcsource/clangjobrequest.cpp @@ -34,6 +34,8 @@ static const char *JobRequestTypeToText(JobRequest::Type type) { switch (type) { RETURN_TEXT_FOR_CASE(UpdateDocumentAnnotations); + RETURN_TEXT_FOR_CASE(ParseSupportiveTranslationUnit); + RETURN_TEXT_FOR_CASE(ReparseSupportiveTranslationUnit); RETURN_TEXT_FOR_CASE(CreateInitialDocumentPreamble); RETURN_TEXT_FOR_CASE(CompleteCode); RETURN_TEXT_FOR_CASE(RequestDocumentAnnotations); @@ -49,6 +51,7 @@ const char *preferredTranslationUnitToText(PreferredTranslationUnit type) switch (type) { RETURN_TEXT_FOR_CASE(RecentlyParsed); RETURN_TEXT_FOR_CASE(PreviouslyParsed); + RETURN_TEXT_FOR_CASE(LastUninitialized); } return "UnhandledPreferredTranslationUnitType"; @@ -93,6 +96,8 @@ JobRequest::Requirements JobRequest::requirementsForType(Type type) |JobRequest::CurrentDocumentRevision); case JobRequest::Type::CompleteCode: case JobRequest::Type::CreateInitialDocumentPreamble: + case JobRequest::Type::ParseSupportiveTranslationUnit: + case JobRequest::Type::ReparseSupportiveTranslationUnit: return JobRequest::Requirements(JobRequest::DocumentValid); } diff --git a/src/tools/clangbackend/ipcsource/clangjobrequest.h b/src/tools/clangbackend/ipcsource/clangjobrequest.h index f9a5604979a..b744f119780 100644 --- a/src/tools/clangbackend/ipcsource/clangjobrequest.h +++ b/src/tools/clangbackend/ipcsource/clangjobrequest.h @@ -34,14 +34,22 @@ #include #include +#include + namespace ClangBackEnd { +class Document; + class JobRequest { public: enum class Type { UpdateDocumentAnnotations, CreateInitialDocumentPreamble, + + ParseSupportiveTranslationUnit, + ReparseSupportiveTranslationUnit, + CompleteCode, RequestDocumentAnnotations, }; @@ -83,6 +91,9 @@ public: }; using JobRequests = QVector; +using JobRequestCreator = std::function; QDebug operator<<(QDebug debug, const JobRequest &jobRequest); diff --git a/src/tools/clangbackend/ipcsource/clangjobs.cpp b/src/tools/clangbackend/ipcsource/clangjobs.cpp index bd5a982d014..fee7c0097a1 100644 --- a/src/tools/clangbackend/ipcsource/clangjobs.cpp +++ b/src/tools/clangbackend/ipcsource/clangjobs.cpp @@ -120,12 +120,22 @@ void Jobs::onJobFinished(IAsyncJob *asyncJob) { qCDebug(jobsLog) << "Finishing" << asyncJob->context().jobRequest; + if (m_jobFinishedCallback) { + const RunningJob runningJob = m_running.value(asyncJob); + m_jobFinishedCallback(runningJob); + } + m_running.remove(asyncJob); delete asyncJob; process(); } +void Jobs::setJobFinishedCallback(const JobFinishedCallback &jobFinishedCallback) +{ + m_jobFinishedCallback = jobFinishedCallback; +} + QList Jobs::runningJobs() const { return m_running.values(); diff --git a/src/tools/clangbackend/ipcsource/clangjobs.h b/src/tools/clangbackend/ipcsource/clangjobs.h index 21c3576a48f..1faf2e3dbdd 100644 --- a/src/tools/clangbackend/ipcsource/clangjobs.h +++ b/src/tools/clangbackend/ipcsource/clangjobs.h @@ -31,6 +31,8 @@ #include +#include + namespace ClangBackEnd { class ClangCodeModelClientInterface; @@ -46,7 +48,9 @@ public: Utf8String translationUnitId; QFuture future; }; + using RunningJobs = QHash; + using JobFinishedCallback = std::function; public: Jobs(Documents &documents, @@ -59,6 +63,8 @@ public: JobRequests process(); + void setJobFinishedCallback(const JobFinishedCallback &jobFinishedCallback); + public /*for tests*/: QList runningJobs() const; JobRequests queue() const; @@ -76,6 +82,7 @@ private: JobQueue m_queue; RunningJobs m_running; + JobFinishedCallback m_jobFinishedCallback; }; } // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangparsesupportivetranslationunitjob.cpp b/src/tools/clangbackend/ipcsource/clangparsesupportivetranslationunitjob.cpp new file mode 100644 index 00000000000..fa8937c1951 --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangparsesupportivetranslationunitjob.cpp @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "clangparsesupportivetranslationunitjob.h" + +#include + +#include + +namespace ClangBackEnd { + +static ParseSupportiveTranslationUnitJob::AsyncResult runAsyncHelper( + const TranslationUnit &translationUnit, + const TranslationUnitUpdateInput &translationUnitUpdateInput) +{ + TIME_SCOPE_DURATION("ParseSupportiveTranslationUnitJob"); + + TranslationUnitUpdateInput updateInput = translationUnitUpdateInput; + updateInput.parseNeeded = true; + + ParseSupportiveTranslationUnitJob::AsyncResult asyncResult; + asyncResult.updateResult = translationUnit.update(updateInput); + + return asyncResult; +} + +IAsyncJob::AsyncPrepareResult ParseSupportiveTranslationUnitJob::prepareAsyncRun() +{ + const JobRequest jobRequest = context().jobRequest; + QTC_ASSERT(jobRequest.type == JobRequest::Type::ParseSupportiveTranslationUnit, return AsyncPrepareResult()); + + try { + m_pinnedDocument = context().documentForJobRequest(); + m_pinnedFileContainer = m_pinnedDocument.fileContainer(); + + const TranslationUnit translationUnit + = m_pinnedDocument.translationUnit(jobRequest.preferredTranslationUnit); + const TranslationUnitUpdateInput updateInput = m_pinnedDocument.createUpdateInput(); + setRunner([translationUnit, updateInput]() { + return runAsyncHelper(translationUnit, updateInput); + }); + return AsyncPrepareResult{translationUnit.id()}; + + } catch (const std::exception &exception) { + qWarning() << "Error in ParseForSupportiveTranslationUnitJob::prepareAsyncRun:" + << exception.what(); + return AsyncPrepareResult(); + } +} + +void ParseSupportiveTranslationUnitJob::finalizeAsyncRun() +{ +} + +} // namespace ClangBackEnd + diff --git a/src/tools/clangbackend/ipcsource/clangparsesupportivetranslationunitjob.h b/src/tools/clangbackend/ipcsource/clangparsesupportivetranslationunitjob.h new file mode 100644 index 00000000000..6d4d01131e2 --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangparsesupportivetranslationunitjob.h @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "clangdocument.h" + +namespace ClangBackEnd { + +struct ParseSupportiveTranslationUnitJobResult +{ + TranslationUnitUpdateResult updateResult; +}; + +class ParseSupportiveTranslationUnitJob : public AsyncJob +{ +public: + using AsyncResult = ParseSupportiveTranslationUnitJobResult; + + AsyncPrepareResult prepareAsyncRun() override; + void finalizeAsyncRun() override; + +private: + Document m_pinnedDocument; + FileContainer m_pinnedFileContainer; +}; + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangreparsesupportivetranslationunitjob.cpp b/src/tools/clangbackend/ipcsource/clangreparsesupportivetranslationunitjob.cpp new file mode 100644 index 00000000000..da922db8e9d --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangreparsesupportivetranslationunitjob.cpp @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "clangreparsesupportivetranslationunitjob.h" + +#include + +#include + +namespace ClangBackEnd { + +static ReparseSupportiveTranslationUnitJob::AsyncResult runAsyncHelper( + const TranslationUnit &translationUnit, + const TranslationUnitUpdateInput &translationUnitUpdateInput) +{ + TIME_SCOPE_DURATION("ReparseSupportiveTranslationUnitJob"); + + TranslationUnitUpdateInput updateInput = translationUnitUpdateInput; + updateInput.reparseNeeded = true; + + ReparseSupportiveTranslationUnitJob::AsyncResult asyncResult; + asyncResult.updateResult = translationUnit.reparse(updateInput); + + return asyncResult; +} + +IAsyncJob::AsyncPrepareResult ReparseSupportiveTranslationUnitJob::prepareAsyncRun() +{ + const JobRequest jobRequest = context().jobRequest; + QTC_ASSERT(jobRequest.type == JobRequest::Type::ReparseSupportiveTranslationUnit, return AsyncPrepareResult()); + + try { + m_pinnedDocument = context().documentForJobRequest(); + m_pinnedFileContainer = m_pinnedDocument.fileContainer(); + + const TranslationUnit translationUnit + = m_pinnedDocument.translationUnit(jobRequest.preferredTranslationUnit); + const TranslationUnitUpdateInput updateInput = m_pinnedDocument.createUpdateInput(); + setRunner([translationUnit, updateInput]() { + return runAsyncHelper(translationUnit, updateInput); + }); + return AsyncPrepareResult{translationUnit.id()}; + + } catch (const std::exception &exception) { + qWarning() << "Error in ReparseSupportiveTranslationUnitJob::prepareAsyncRun:" + << exception.what(); + return AsyncPrepareResult(); + } +} + +void ReparseSupportiveTranslationUnitJob::finalizeAsyncRun() +{ + if (!context().isOutdated()) { + const AsyncResult result = asyncResult(); + m_pinnedDocument.incorporateUpdaterResult(result.updateResult); + } +} + +} // namespace ClangBackEnd + diff --git a/src/tools/clangbackend/ipcsource/clangreparsesupportivetranslationunitjob.h b/src/tools/clangbackend/ipcsource/clangreparsesupportivetranslationunitjob.h new file mode 100644 index 00000000000..1b352c2c361 --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangreparsesupportivetranslationunitjob.h @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "clangdocument.h" + +namespace ClangBackEnd { + +struct ReparseSupportiveTranslationUnitJobResult +{ + TranslationUnitUpdateResult updateResult; +}; + +class ReparseSupportiveTranslationUnitJob : public AsyncJob +{ +public: + using AsyncResult = ReparseSupportiveTranslationUnitJobResult; + + AsyncPrepareResult prepareAsyncRun() override; + void finalizeAsyncRun() override; + +private: + Document m_pinnedDocument; + FileContainer m_pinnedFileContainer; +}; + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangrequestdocumentannotationsjob.cpp b/src/tools/clangbackend/ipcsource/clangrequestdocumentannotationsjob.cpp index e53ff0b8007..2fd806b192b 100644 --- a/src/tools/clangbackend/ipcsource/clangrequestdocumentannotationsjob.cpp +++ b/src/tools/clangbackend/ipcsource/clangrequestdocumentannotationsjob.cpp @@ -57,7 +57,8 @@ IAsyncJob::AsyncPrepareResult RequestDocumentAnnotationsJob::prepareAsyncRun() m_pinnedDocument = context().documentForJobRequest(); m_pinnedFileContainer = m_pinnedDocument.fileContainer(); - const TranslationUnit translationUnit = m_pinnedDocument.translationUnit(); + const TranslationUnit translationUnit + = m_pinnedDocument.translationUnit(jobRequest.preferredTranslationUnit); setRunner([translationUnit]() { return runAsyncHelper(translationUnit); }); diff --git a/src/tools/clangbackend/ipcsource/clangsupportivetranslationunitinitializer.cpp b/src/tools/clangbackend/ipcsource/clangsupportivetranslationunitinitializer.cpp new file mode 100644 index 00000000000..d421ca05cbb --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangsupportivetranslationunitinitializer.cpp @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "clangsupportivetranslationunitinitializer.h" + +#include "clangjobs.h" +#include "clangtranslationunits.h" + +#include + +namespace ClangBackEnd { + + // TODO: Check translation unit id? + +SupportiveTranslationUnitInitializer::SupportiveTranslationUnitInitializer( + const Document &document, + Jobs &jobs) + : m_document(document) + , m_jobs(jobs) +{ +} + +void SupportiveTranslationUnitInitializer::setJobRequestCreator(const JobRequestCreator &creator) +{ + m_jobRequestCreator = creator; +} + +void SupportiveTranslationUnitInitializer::setIsDocumentClosedChecker( + const IsDocumentClosedChecker &isDocumentClosedChecker) +{ + m_isDocumentClosedChecker = isDocumentClosedChecker; +} + +SupportiveTranslationUnitInitializer::State SupportiveTranslationUnitInitializer::state() const +{ + return m_state; +} + +void SupportiveTranslationUnitInitializer::startInitializing() +{ + QTC_CHECK(m_state == State::NotInitialized); + if (abortIfDocumentIsClosed()) + return; + + m_document.translationUnits().createAndAppend(); + + m_jobs.setJobFinishedCallback([this](const Jobs::RunningJob &runningJob) { + checkIfParseJobFinished(runningJob); + }); + addJob(JobRequest::Type::ParseSupportiveTranslationUnit); + m_jobs.process(); + + m_state = State::WaitingForParseJob; +} + +void SupportiveTranslationUnitInitializer::checkIfParseJobFinished(const Jobs::RunningJob &job) +{ + QTC_CHECK(m_state == State::WaitingForParseJob); + if (abortIfDocumentIsClosed()) + return; + + if (job.jobRequest.type == JobRequest::Type::ParseSupportiveTranslationUnit) { + m_jobs.setJobFinishedCallback([this](const Jobs::RunningJob &runningJob) { + checkIfReparseJobFinished(runningJob); + }); + + addJob(JobRequest::Type::ReparseSupportiveTranslationUnit); + + m_state = State::WaitingForReparseJob; + } +} + +void SupportiveTranslationUnitInitializer::checkIfReparseJobFinished(const Jobs::RunningJob &job) +{ + QTC_CHECK(m_state == State::WaitingForReparseJob); + if (abortIfDocumentIsClosed()) + return; + + if (job.jobRequest.type == JobRequest::Type::ReparseSupportiveTranslationUnit) { + if (m_document.translationUnits().areAllTranslationUnitsParsed()) { + m_jobs.setJobFinishedCallback(nullptr); + m_state = State::Initialized; + } else { + // The supportive translation unit was reparsed, but the document + // revision changed in the meanwhile, so try again. + addJob(JobRequest::Type::ReparseSupportiveTranslationUnit); + } + } +} + +bool SupportiveTranslationUnitInitializer::abortIfDocumentIsClosed() +{ + QTC_CHECK(m_isDocumentClosedChecker); + + if (m_isDocumentClosedChecker(m_document.filePath(), m_document.projectPartId())) { + m_state = State::Aborted; + return true; + } + + return false; +} + +void SupportiveTranslationUnitInitializer::addJob(JobRequest::Type jobRequestType) +{ + QTC_CHECK(m_jobRequestCreator); + + const JobRequest jobRequest = m_jobRequestCreator(m_document, + jobRequestType, + PreferredTranslationUnit::LastUninitialized); + + m_jobs.add(jobRequest); +} + +void SupportiveTranslationUnitInitializer::setState(const State &state) +{ + m_state = state; +} + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangsupportivetranslationunitinitializer.h b/src/tools/clangbackend/ipcsource/clangsupportivetranslationunitinitializer.h new file mode 100644 index 00000000000..f65cc4afd22 --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangsupportivetranslationunitinitializer.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "clangdocument.h" +#include "clangjobrequest.h" +#include "clangjobs.h" + +#include + +#pragma once + +namespace ClangBackEnd { + +class SupportiveTranslationUnitInitializer +{ +public: + using IsDocumentClosedChecker = std::function; + + enum class State { + NotInitialized, + WaitingForParseJob, + WaitingForReparseJob, + Initialized, + Aborted + }; + +public: + SupportiveTranslationUnitInitializer(const Document &document, Jobs &jobs); + + void setJobRequestCreator(const JobRequestCreator &creator); + void setIsDocumentClosedChecker(const IsDocumentClosedChecker &isDocumentClosedChecker); + + State state() const; + void startInitializing(); + +public: // for tests + void setState(const State &state); + void checkIfParseJobFinished(const Jobs::RunningJob &job); + void checkIfReparseJobFinished(const Jobs::RunningJob &job); + +private: + + bool abortIfDocumentIsClosed(); + void addJob(JobRequest::Type jobRequestType); + +private: + Document m_document; + Jobs &m_jobs; + + State m_state = State::NotInitialized; + JobRequestCreator m_jobRequestCreator; + IsDocumentClosedChecker m_isDocumentClosedChecker; +}; + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangtranslationunits.cpp b/src/tools/clangbackend/ipcsource/clangtranslationunits.cpp index 394d653e483..7f65a9bf247 100644 --- a/src/tools/clangbackend/ipcsource/clangtranslationunits.cpp +++ b/src/tools/clangbackend/ipcsource/clangtranslationunits.cpp @@ -70,10 +70,15 @@ TranslationUnit TranslationUnits::get(PreferredTranslationUnit type) if (m_tuDatas.isEmpty()) throw TranslationUnitDoesNotExist(m_filePath); - if (m_tuDatas.size() == 1 || !areAllTranslationUnitsParsed()) + if (m_tuDatas.size() == 1) return toTranslationUnit(m_tuDatas.first()); - return getPreferredTranslationUnit(type); + if (areAllTranslationUnitsParsed()) + return getPreferredTranslationUnit(type); + else if (type == PreferredTranslationUnit::LastUninitialized) + return toTranslationUnit(m_tuDatas.last()); + + return toTranslationUnit(m_tuDatas.first()); } void TranslationUnits::updateParseTimePoint(const Utf8String &translationUnitId, @@ -89,6 +94,11 @@ void TranslationUnits::updateParseTimePoint(const Utf8String &translationUnitId, << "PreviouslyParsed:" << get(PreferredTranslationUnit::PreviouslyParsed).id(); } +TimePoint TranslationUnits::parseTimePoint(const Utf8String &translationUnitId) +{ + return findUnit(translationUnitId).parseTimePoint; +} + bool TranslationUnits::areAllTranslationUnitsParsed() const { return Utils::allOf(m_tuDatas, [](const TranslationUnitData &unit) { @@ -96,6 +106,11 @@ bool TranslationUnits::areAllTranslationUnitsParsed() const }); } +int TranslationUnits::size() const +{ + return m_tuDatas.size(); +} + TranslationUnit TranslationUnits::getPreferredTranslationUnit(PreferredTranslationUnit type) { using TuData = TranslationUnitData; diff --git a/src/tools/clangbackend/ipcsource/clangtranslationunits.h b/src/tools/clangbackend/ipcsource/clangtranslationunits.h index 77f771cd98a..5a9d02f0e37 100644 --- a/src/tools/clangbackend/ipcsource/clangtranslationunits.h +++ b/src/tools/clangbackend/ipcsource/clangtranslationunits.h @@ -63,8 +63,13 @@ public: TranslationUnit get(PreferredTranslationUnit type = PreferredTranslationUnit::RecentlyParsed); void updateParseTimePoint(const Utf8String &translationUnitId, TimePoint timePoint); -private: bool areAllTranslationUnitsParsed() const; + +public: // for tests + int size() const; + TimePoint parseTimePoint(const Utf8String &translationUnitId); + +private: TranslationUnit getPreferredTranslationUnit(PreferredTranslationUnit type); TranslationUnitData &findUnit(const Utf8String &translationUnitId); TranslationUnit toTranslationUnit(TranslationUnitData &unit); diff --git a/src/tools/clangbackend/ipcsource/clangupdatedocumentannotationsjob.cpp b/src/tools/clangbackend/ipcsource/clangupdatedocumentannotationsjob.cpp index 94fb8314bc3..4ece1fb2a0e 100644 --- a/src/tools/clangbackend/ipcsource/clangupdatedocumentannotationsjob.cpp +++ b/src/tools/clangbackend/ipcsource/clangupdatedocumentannotationsjob.cpp @@ -62,7 +62,8 @@ IAsyncJob::AsyncPrepareResult UpdateDocumentAnnotationsJob::prepareAsyncRun() m_pinnedDocument = context().documentForJobRequest(); m_pinnedFileContainer = m_pinnedDocument.fileContainer(); - const TranslationUnit translationUnit = m_pinnedDocument.translationUnit(); + const TranslationUnit translationUnit + = m_pinnedDocument.translationUnit(jobRequest.preferredTranslationUnit); const TranslationUnitUpdateInput updateInput = m_pinnedDocument.createUpdateInput(); setRunner([translationUnit, updateInput]() { return runAsyncHelper(translationUnit, updateInput); diff --git a/tests/unit/unittest/clangipcserver-test.cpp b/tests/unit/unittest/clangipcserver-test.cpp index 3aa7ed33f38..4ce972e3ed3 100644 --- a/tests/unit/unittest/clangipcserver-test.cpp +++ b/tests/unit/unittest/clangipcserver-test.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -135,6 +136,8 @@ protected: void completeCodeInFileA(); void completeCodeInFileB(); + bool isSupportiveTranslationUnitInitialized(const Utf8String &filePath); + void expectDocumentAnnotationsChanged(int count); void expectCompletion(const CodeCompletion &completion); void expectCompletionFromFileA(); @@ -286,6 +289,50 @@ TEST_F(ClangClangCodeModelServer, SetCurrentAndVisibleEditor) ASSERT_TRUE(functionDocument.isVisibleInEditor()); } +TEST_F(ClangClangCodeModelServer, StartCompletionJobFirstOnEditThatTriggersCompletion) +{ + registerProjectAndFile(filePathA, 2); + ASSERT_TRUE(waitUntilAllJobsFinished()); + expectCompletionFromFileA(); + + updateUnsavedContent(filePathA, unsavedContent(filePathAUnsavedVersion2), 1); + completeCodeInFileA(); + + const QList jobs = clangServer.runningJobsForTestsOnly(); + ASSERT_THAT(jobs.size(), Eq(1)); + ASSERT_THAT(jobs.first().jobRequest.type, Eq(JobRequest::Type::CompleteCode)); +} + +TEST_F(ClangClangCodeModelServer, SupportiveTranslationUnitNotInitializedAfterRegister) +{ + registerProjectAndFile(filePathA, 1); + + ASSERT_TRUE(waitUntilAllJobsFinished()); + ASSERT_FALSE(isSupportiveTranslationUnitInitialized(filePathA)); +} + +TEST_F(ClangClangCodeModelServer, SupportiveTranslationUnitIsSetupAfterFirstEdit) +{ + registerProjectAndFile(filePathA, 2); + ASSERT_TRUE(waitUntilAllJobsFinished()); + + updateUnsavedContent(filePathA, unsavedContent(filePathAUnsavedVersion2), 1); + + ASSERT_TRUE(waitUntilAllJobsFinished()); + ASSERT_TRUE(isSupportiveTranslationUnitInitialized(filePathA)); +} + +TEST_F(ClangClangCodeModelServer, OpenDocumentAndEdit) +{ + registerProjectAndFile(filePathA, 4); + ASSERT_TRUE(waitUntilAllJobsFinished()); + + for (unsigned revision = 1; revision <= 3; ++revision) { + updateUnsavedContent(filePathA, unsavedContent(filePathAUnsavedVersion2), revision); + ASSERT_TRUE(waitUntilAllJobsFinished()); + } +} + TEST_F(ClangClangCodeModelServer, IsNotCurrentCurrentAndVisibleEditorAnymore) { registerProjectAndFilesAndWaitForFinished(); @@ -411,6 +458,16 @@ void ClangClangCodeModelServer::completeCodeInFileB() completeCode(filePathB, 35, 1); } +bool ClangClangCodeModelServer::isSupportiveTranslationUnitInitialized(const Utf8String &filePath) +{ + Document document = clangServer.documentsForTestOnly().document(filePath, projectPartId); + DocumentProcessor documentProcessor = clangServer.documentProcessors().processor(document); + + return document.translationUnits().size() == 2 + && documentProcessor.hasSupportiveTranslationUnit() + && documentProcessor.isSupportiveTranslationUnitInitialized(); +} + void ClangClangCodeModelServer::expectCompletion(const CodeCompletion &completion) { EXPECT_CALL(mockClangCodeModelClient, diff --git a/tests/unit/unittest/clangparsesupportivetranslationunitjobtest.cpp b/tests/unit/unittest/clangparsesupportivetranslationunitjobtest.cpp new file mode 100644 index 00000000000..4b7d5bc23bf --- /dev/null +++ b/tests/unit/unittest/clangparsesupportivetranslationunitjobtest.cpp @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 +#include + +using namespace ClangBackEnd; + +using testing::Eq; +using testing::Not; +using testing::_; + +namespace { + +class ParseSupportiveTranslationUnitJob : public ClangAsyncJobTest +{ +protected: + void SetUp() override { BaseSetUp(JobRequest::Type::ParseSupportiveTranslationUnit, job); } + + TimePoint parseTimePointOfDocument(); + +protected: + ClangBackEnd::ParseSupportiveTranslationUnitJob job; +}; + +TEST_F(ParseSupportiveTranslationUnitJob, PrepareAsyncRun) +{ + job.setContext(jobContext); + + ASSERT_TRUE(job.prepareAsyncRun()); +} + +TEST_F(ParseSupportiveTranslationUnitJob, RunAsync) +{ + job.setContext(jobContext); + job.prepareAsyncRun(); + + job.runAsync(); + + ASSERT_TRUE(waitUntilJobFinished(job)); +} + +TEST_F(ParseSupportiveTranslationUnitJob, DoNotIncorporateUpdaterResult) +{ + const TimePoint parseTimePointBefore = parseTimePointOfDocument(); + job.setContext(jobContext); + job.prepareAsyncRun(); + + job.runAsync(); + + ASSERT_TRUE(waitUntilJobFinished(job)); + ASSERT_THAT(parseTimePointOfDocument(), Eq(parseTimePointBefore)); +} + +TimePoint ParseSupportiveTranslationUnitJob::parseTimePointOfDocument() +{ + const Utf8String translationUnitId = document.translationUnit().id(); + + return document.translationUnits().parseTimePoint(translationUnitId); +} + +} // anonymous diff --git a/tests/unit/unittest/clangreparsesupportivetranslationunitjobtest.cpp b/tests/unit/unittest/clangreparsesupportivetranslationunitjobtest.cpp new file mode 100644 index 00000000000..1796b183c42 --- /dev/null +++ b/tests/unit/unittest/clangreparsesupportivetranslationunitjobtest.cpp @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 +#include + +using namespace ClangBackEnd; + +using testing::Eq; +using testing::Not; +using testing::_; + +namespace { + +class ReparseSupportiveTranslationUnitJob : public ClangAsyncJobTest +{ +protected: + void SetUp() override { BaseSetUp(JobRequest::Type::ReparseSupportiveTranslationUnit, job); } + + TimePoint parseTimePointOfDocument(); + void parse(); + +protected: + ClangBackEnd::ReparseSupportiveTranslationUnitJob job; +}; + +TEST_F(ReparseSupportiveTranslationUnitJob, PrepareAsyncRun) +{ + job.setContext(jobContext); + + ASSERT_TRUE(job.prepareAsyncRun()); +} + +TEST_F(ReparseSupportiveTranslationUnitJob, RunAsync) +{ + parse(); + job.setContext(jobContext); + job.prepareAsyncRun(); + + job.runAsync(); + + ASSERT_TRUE(waitUntilJobFinished(job)); +} + +TEST_F(ReparseSupportiveTranslationUnitJob, IncorporateUpdaterResult) +{ + parse(); + const TimePoint parseTimePointBefore = parseTimePointOfDocument(); + job.setContext(jobContext); + job.prepareAsyncRun(); + + job.runAsync(); + + ASSERT_TRUE(waitUntilJobFinished(job)); + ASSERT_THAT(parseTimePointOfDocument(), Not(Eq(parseTimePointBefore))); +} + +TEST_F(ReparseSupportiveTranslationUnitJob, DoNotIncorporateUpdaterResultIfDocumentWasClosed) +{ + parse(); + const TimePoint parseTimePointBefore = parseTimePointOfDocument(); + job.setContext(jobContext); + job.prepareAsyncRun(); + + job.runAsync(); + documents.remove({FileContainer{filePath, projectPartId}}); + + ASSERT_TRUE(waitUntilJobFinished(job)); + ASSERT_THAT(parseTimePointOfDocument(), Eq(parseTimePointBefore)); +} + +TimePoint ReparseSupportiveTranslationUnitJob::parseTimePointOfDocument() +{ + const Utf8String translationUnitId = document.translationUnit().id(); + + return document.translationUnits().parseTimePoint(translationUnitId); +} + +void ReparseSupportiveTranslationUnitJob::parse() +{ + projects.createOrUpdate({ProjectPartContainer{projectPartId, Utf8StringVector()}}); + document.parse(); +} + +} // anonymous diff --git a/tests/unit/unittest/clangsupportivetranslationunitinitializertest.cpp b/tests/unit/unittest/clangsupportivetranslationunitinitializertest.cpp new file mode 100644 index 00000000000..dcb970e4100 --- /dev/null +++ b/tests/unit/unittest/clangsupportivetranslationunitinitializertest.cpp @@ -0,0 +1,262 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "dummyclangipcclient.h" +#include "processevents-utilities.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include "gtest-qt-printing.h" + +using namespace ClangBackEnd; + +using testing::Eq; + +namespace { + +class Data { +public: + Data() + { + projects.createOrUpdate({ProjectPartContainer(projectPartId)}); + + const QVector fileContainer{FileContainer(filePath, projectPartId)}; + document = documents.create(fileContainer).front(); + documents.setVisibleInEditors({filePath}); + documents.setUsedByCurrentEditor(filePath); + + const auto isDocumentClosed = [this](const Utf8String &filePath, + const Utf8String &projectPartId) { + return !documents.hasDocument(filePath, projectPartId); + }; + const auto jobRequestCreator = [this](const Document &document, + JobRequest::Type type, + PreferredTranslationUnit preferredTranslationUnit) { + return createJobRequest(document, type, preferredTranslationUnit); + }; + initializer.reset(new ClangBackEnd::SupportiveTranslationUnitInitializer{document, jobs}); + initializer->setIsDocumentClosedChecker(isDocumentClosed); + initializer->setJobRequestCreator(jobRequestCreator); + } + + JobRequest createJobRequest(const Document &document, + JobRequest::Type type, + PreferredTranslationUnit preferredTranslationUnit) const + { + JobRequest jobRequest; + jobRequest.type = type; + jobRequest.requirements = JobRequest::requirementsForType(type); + jobRequest.filePath = document.filePath(); + jobRequest.projectPartId = document.projectPartId(); + jobRequest.unsavedFilesChangeTimePoint = unsavedFiles.lastChangeTimePoint(); + jobRequest.documentRevision = document.documentRevision(); + jobRequest.preferredTranslationUnit = preferredTranslationUnit; + const ProjectPart &projectPart = projects.project(document.projectPartId()); + jobRequest.projectChangeTimePoint = projectPart.lastChangeTimePoint(); + + return jobRequest; + } + +public: + Utf8String filePath{Utf8StringLiteral(TESTDATA_DIR"/translationunits.cpp")}; + Utf8String projectPartId{Utf8StringLiteral("/path/to/projectfile")}; + + ProjectParts projects; + UnsavedFiles unsavedFiles; + Documents documents{projects, unsavedFiles}; + Document document; + DummyIpcClient dummyClientInterface; + + Jobs jobs{documents, unsavedFiles, projects, dummyClientInterface}; + + std::unique_ptr initializer; +}; + +class SupportiveTranslationUnitInitializer : public ::testing::Test +{ +protected: + void parse(); + Jobs::RunningJob createRunningJob(JobRequest::Type type) const; + + void assertNoJobIsRunningAndEmptyQueue(); + void assertSingleJobRunningAndEmptyQueue(); + + bool waitUntilJobChainFinished(int timeOutInMs = 10000) const; + +protected: + Data d; + + Utf8String &filePath{d.filePath}; + Utf8String &projectPartId{d.projectPartId}; + + ProjectParts projects{d.projects}; + Document &document{d.document}; + Documents &documents{d.documents}; + Jobs &jobs{d.jobs}; + ClangBackEnd::SupportiveTranslationUnitInitializer &initializer{*d.initializer}; +}; + +TEST_F(SupportiveTranslationUnitInitializer, HasInitiallyNotInitializedState) +{ + ASSERT_THAT(initializer.state(), Eq(ClangBackEnd::SupportiveTranslationUnitInitializer::State::NotInitialized)); +} + +TEST_F(SupportiveTranslationUnitInitializer, StartInitializingAbortsIfDocumentIsClosed) +{ + documents.remove({FileContainer(filePath, projectPartId)}); + + initializer.startInitializing(); + + assertNoJobIsRunningAndEmptyQueue(); + ASSERT_THAT(initializer.state(), Eq(ClangBackEnd::SupportiveTranslationUnitInitializer::State::Aborted)); +} + +TEST_F(SupportiveTranslationUnitInitializer, StartInitializingAddsTranslationUnit) +{ + initializer.startInitializing(); + + ASSERT_THAT(document.translationUnits().size(), Eq(2)); + ASSERT_FALSE(document.translationUnits().areAllTranslationUnitsParsed()); +} + +TEST_F(SupportiveTranslationUnitInitializer, StartInitializingStartsJob) +{ + initializer.startInitializing(); + + assertSingleJobRunningAndEmptyQueue(); + const Jobs::RunningJob runningJob = jobs.runningJobs().first(); + ASSERT_THAT(runningJob.jobRequest.type, JobRequest::Type::ParseSupportiveTranslationUnit); +} + +TEST_F(SupportiveTranslationUnitInitializer, CheckIfParseJobFinishedAbortsIfDocumentIsClosed) +{ + documents.remove({FileContainer(filePath, projectPartId)}); + initializer.setState(ClangBackEnd::SupportiveTranslationUnitInitializer::State::WaitingForParseJob); + const Jobs::RunningJob runningJob = createRunningJob(JobRequest::Type::ParseSupportiveTranslationUnit); + + initializer.checkIfParseJobFinished(runningJob); + + assertNoJobIsRunningAndEmptyQueue(); + ASSERT_THAT(initializer.state(), Eq(ClangBackEnd::SupportiveTranslationUnitInitializer::State::Aborted)); +} + +TEST_F(SupportiveTranslationUnitInitializer, CheckIfParseJobFinishedStartsJob) +{ + parse(); + initializer.setState(ClangBackEnd::SupportiveTranslationUnitInitializer::State::WaitingForParseJob); + Jobs::RunningJob runningJob = createRunningJob(JobRequest::Type::ParseSupportiveTranslationUnit); + + initializer.checkIfParseJobFinished(runningJob); + jobs.process(); + + assertSingleJobRunningAndEmptyQueue(); + runningJob = jobs.runningJobs().first(); + ASSERT_THAT(runningJob.jobRequest.type, JobRequest::Type::ReparseSupportiveTranslationUnit); +} + +TEST_F(SupportiveTranslationUnitInitializer, CheckIfReparseJobFinishedAbortsIfDocumentIsClosed) +{ + documents.remove({FileContainer(filePath, projectPartId)}); + initializer.setState(ClangBackEnd::SupportiveTranslationUnitInitializer::State::WaitingForReparseJob); + const Jobs::RunningJob runningJob = createRunningJob(JobRequest::Type::ReparseSupportiveTranslationUnit); + + initializer.checkIfReparseJobFinished(runningJob); + + assertNoJobIsRunningAndEmptyQueue(); + ASSERT_THAT(initializer.state(), Eq(ClangBackEnd::SupportiveTranslationUnitInitializer::State::Aborted)); +} + +TEST_F(SupportiveTranslationUnitInitializer, CheckIfReparseJobFinishedStartsJob) +{ + parse(); + initializer.setState(ClangBackEnd::SupportiveTranslationUnitInitializer::State::WaitingForReparseJob); + Jobs::RunningJob runningJob = createRunningJob(JobRequest::Type::ReparseSupportiveTranslationUnit); + + initializer.checkIfReparseJobFinished(runningJob); + jobs.process(); + + assertNoJobIsRunningAndEmptyQueue(); + ASSERT_THAT(initializer.state(), Eq(ClangBackEnd::SupportiveTranslationUnitInitializer::State::Initialized)); +} + +TEST_F(SupportiveTranslationUnitInitializer, FullRun) +{ + parse(); + initializer.startInitializing(); + + waitUntilJobChainFinished(); + ASSERT_THAT(initializer.state(), Eq(ClangBackEnd::SupportiveTranslationUnitInitializer::State::Initialized)); +} + +void SupportiveTranslationUnitInitializer::parse() +{ + projects.createOrUpdate({ProjectPartContainer{projectPartId, Utf8StringVector()}}); + document.parse(); +} + +Jobs::RunningJob SupportiveTranslationUnitInitializer::createRunningJob(JobRequest::Type type) const +{ + const JobRequest jobRequest = d.createJobRequest(document, + type, + PreferredTranslationUnit::LastUninitialized); + return Jobs::RunningJob{jobRequest, Utf8String(), QFuture()}; +} + +void SupportiveTranslationUnitInitializer::assertNoJobIsRunningAndEmptyQueue() +{ + ASSERT_TRUE(jobs.runningJobs().isEmpty()); + ASSERT_TRUE(jobs.queue().isEmpty()); +} + +void SupportiveTranslationUnitInitializer::assertSingleJobRunningAndEmptyQueue() +{ + ASSERT_THAT(jobs.runningJobs().size(), Eq(1)); + ASSERT_TRUE(jobs.queue().isEmpty()); +} + +bool SupportiveTranslationUnitInitializer::waitUntilJobChainFinished(int timeOutInMs) const +{ + const auto noJobsRunningAnymore = [this]() { + return jobs.runningJobs().isEmpty() && jobs.queue().isEmpty(); + }; + + return ProcessEventUtilities::processEventsUntilTrue(noJobsRunningAnymore, timeOutInMs); +} + +} // anonymous namespace diff --git a/tests/unit/unittest/clangtranslationunits-test.cpp b/tests/unit/unittest/clangtranslationunits-test.cpp index ddd386b6bf0..d89f3ba83e0 100644 --- a/tests/unit/unittest/clangtranslationunits-test.cpp +++ b/tests/unit/unittest/clangtranslationunits-test.cpp @@ -106,6 +106,17 @@ TEST_F(TranslationUnits, GetFirstForMultipleTranslationUnitsAndOnlySecondParsed) ASSERT_THAT(queried.id(), Eq(created1.id())); } +TEST_F(TranslationUnits, GetLastUnitializedForMultipleTranslationUnits) +{ + const TranslationUnit created1 = translationUnits.createAndAppend(); + translationUnits.updateParseTimePoint(created1.id(), Clock::now()); + const TranslationUnit created2 = translationUnits.createAndAppend(); + + const TranslationUnit queried = translationUnits.get(PreferredTranslationUnit::LastUninitialized); + + ASSERT_THAT(queried.id(), Eq(created2.id())); +} + TEST_F(TranslationUnits, GetRecentForMultipleTranslationUnits) { const TranslationUnit created1 = translationUnits.createAndAppend(); diff --git a/tests/unit/unittest/unittest.pro b/tests/unit/unittest/unittest.pro index 168047cca55..e847b8347fa 100644 --- a/tests/unit/unittest/unittest.pro +++ b/tests/unit/unittest/unittest.pro @@ -56,7 +56,10 @@ SOURCES += \ clangisdiagnosticrelatedtolocation-test.cpp \ clangjobqueue-test.cpp \ clangjobs-test.cpp \ + clangparsesupportivetranslationunitjobtest.cpp \ + clangreparsesupportivetranslationunitjobtest.cpp \ clangrequestdocumentannotationsjob-test.cpp \ + clangsupportivetranslationunitinitializertest.cpp \ clangstring-test.cpp \ clangtranslationunits-test.cpp \ clangupdatedocumentannotationsjob-test.cpp \ From bf5c1cc4fba80e0f08ba796d14ff1b611fbbf4f4 Mon Sep 17 00:00:00 2001 From: Nikolai Kosjar Date: Thu, 6 Oct 2016 12:54:22 +0200 Subject: [PATCH 12/27] Clang: Avoid duplicate jobs without changes in-between This could happen, e.g. with this message order: >>> updateTranslationUnitsForEditor() add job<1> run job<1> >>> updateVisibleTranslationUnits(Utf8String(), {}) >>> updateVisibleTranslationUnits(path, {path}) add job<2> finish job<1> run job<2> -- Ops, nothing is changed but job<2> is started This led to an outdated translation unit (e.g. wrong highlighting). Now JobQueue checks for duplicates in the queue and checks all the currently running jobs. Change-Id: I05843fddcbd21ce0489681c283227c0027ded428 Reviewed-by: David Schulz --- .../clangbackend/ipcsource/clangjobqueue.cpp | 38 ++++++++++++++++--- .../clangbackend/ipcsource/clangjobqueue.h | 15 ++++++-- .../ipcsource/clangjobrequest.cpp | 17 +++++++++ .../clangbackend/ipcsource/clangjobrequest.h | 2 + .../clangbackend/ipcsource/clangjobs.cpp | 20 ++++++++-- src/tools/clangbackend/ipcsource/clangjobs.h | 3 +- tests/unit/unittest/clangipcserver-test.cpp | 15 ++++++++ tests/unit/unittest/clangjobqueue-test.cpp | 33 +++++++++++++--- tests/unit/unittest/clangjobs-test.cpp | 2 +- 9 files changed, 124 insertions(+), 21 deletions(-) diff --git a/src/tools/clangbackend/ipcsource/clangjobqueue.cpp b/src/tools/clangbackend/ipcsource/clangjobqueue.cpp index cff2bd3bc05..ceae5c070e6 100644 --- a/src/tools/clangbackend/ipcsource/clangjobqueue.cpp +++ b/src/tools/clangbackend/ipcsource/clangjobqueue.cpp @@ -41,11 +41,22 @@ JobQueue::JobQueue(Documents &documents, ProjectParts &projectParts) { } -void JobQueue::add(const JobRequest &job) +bool JobQueue::add(const JobRequest &job) { - qCDebug(jobsLog) << "Adding" << job; + if (m_queue.contains(job)) { + qCDebug(jobsLog) << "Not adding duplicate request" << job; + return false; + } + if (isJobRunningForJobRequest(job)) { + qCDebug(jobsLog) << "Not adding duplicate request for already running job" << job; + return false; + } + + qCDebug(jobsLog) << "Adding" << job; m_queue.append(job); + + return true; } int JobQueue::size() const @@ -200,15 +211,30 @@ JobRequests JobQueue::takeJobRequestsToRunNow() bool JobQueue::isJobRunningForTranslationUnit(const Utf8String &translationUnitId) { - if (m_isJobRunningHandler) - return m_isJobRunningHandler(translationUnitId); + if (m_isJobRunningForTranslationUnitHandler) + return m_isJobRunningForTranslationUnitHandler(translationUnitId); return false; } -void JobQueue::setIsJobRunningHandler(const IsJobRunningHandler &isJobRunningHandler) +bool JobQueue::isJobRunningForJobRequest(const JobRequest &jobRequest) { - m_isJobRunningHandler = isJobRunningHandler; + if (m_isJobRunningForJobRequestHandler) + return m_isJobRunningForJobRequestHandler(jobRequest); + + return false; +} + +void JobQueue::setIsJobRunningForTranslationUnitHandler( + const IsJobRunningForTranslationUnitHandler &isJobRunningHandler) +{ + m_isJobRunningForTranslationUnitHandler = isJobRunningHandler; +} + +void JobQueue::setIsJobRunningForJobRequestHandler( + const JobQueue::IsJobRunningForJobRequestHandler &isJobRunningHandler) +{ + m_isJobRunningForJobRequestHandler = isJobRunningHandler; } JobRequests JobQueue::queue() const diff --git a/src/tools/clangbackend/ipcsource/clangjobqueue.h b/src/tools/clangbackend/ipcsource/clangjobqueue.h index 3781dda3d36..d48f96aaaeb 100644 --- a/src/tools/clangbackend/ipcsource/clangjobqueue.h +++ b/src/tools/clangbackend/ipcsource/clangjobqueue.h @@ -39,12 +39,17 @@ class JobQueue public: JobQueue(Documents &documents, ProjectParts &projects); - void add(const JobRequest &job); + bool add(const JobRequest &job); JobRequests processQueue(); - using IsJobRunningHandler = std::function; - void setIsJobRunningHandler(const IsJobRunningHandler &isJobRunningHandler); + using IsJobRunningForTranslationUnitHandler = std::function; + void setIsJobRunningForTranslationUnitHandler( + const IsJobRunningForTranslationUnitHandler &isJobRunningHandler); + + using IsJobRunningForJobRequestHandler = std::function; + void setIsJobRunningForJobRequestHandler( + const IsJobRunningForJobRequestHandler &isJobRunningHandler); public: // for tests JobRequests queue() const; @@ -53,6 +58,7 @@ public: // for tests private: bool isJobRunningForTranslationUnit(const Utf8String &translationUnitId); + bool isJobRunningForJobRequest(const JobRequest &jobRequest); JobRequests takeJobRequestsToRunNow(); void removeOutDatedRequests(); bool isJobRequestOutDated(const JobRequest &jobRequest) const; @@ -61,7 +67,8 @@ private: Documents &m_documents; ProjectParts &m_projectParts; - IsJobRunningHandler m_isJobRunningHandler; + IsJobRunningForTranslationUnitHandler m_isJobRunningForTranslationUnitHandler; + IsJobRunningForJobRequestHandler m_isJobRunningForJobRequestHandler; JobRequests m_queue; }; diff --git a/src/tools/clangbackend/ipcsource/clangjobrequest.cpp b/src/tools/clangbackend/ipcsource/clangjobrequest.cpp index 73fe6f4e68b..1065267762e 100644 --- a/src/tools/clangbackend/ipcsource/clangjobrequest.cpp +++ b/src/tools/clangbackend/ipcsource/clangjobrequest.cpp @@ -86,6 +86,23 @@ JobRequest::JobRequest() id = ++idCounter; } +bool JobRequest::operator==(const JobRequest &other) const +{ + return type == other.type + && requirements == other.requirements + + && filePath == other.filePath + && projectPartId == other.projectPartId + && unsavedFilesChangeTimePoint == other.unsavedFilesChangeTimePoint + && projectChangeTimePoint == other.projectChangeTimePoint + && documentRevision == other.documentRevision + && preferredTranslationUnit == other.preferredTranslationUnit + + && line == other.line + && column == other.column + && ticketNumber == other.ticketNumber; +} + JobRequest::Requirements JobRequest::requirementsForType(Type type) { switch (type) { diff --git a/src/tools/clangbackend/ipcsource/clangjobrequest.h b/src/tools/clangbackend/ipcsource/clangjobrequest.h index b744f119780..258763de945 100644 --- a/src/tools/clangbackend/ipcsource/clangjobrequest.h +++ b/src/tools/clangbackend/ipcsource/clangjobrequest.h @@ -71,6 +71,8 @@ public: JobRequest(); + bool operator==(const JobRequest &other) const; + public: quint64 id = 0; Type type; diff --git a/src/tools/clangbackend/ipcsource/clangjobs.cpp b/src/tools/clangbackend/ipcsource/clangjobs.cpp index fee7c0097a1..10cc1293157 100644 --- a/src/tools/clangbackend/ipcsource/clangjobs.cpp +++ b/src/tools/clangbackend/ipcsource/clangjobs.cpp @@ -45,8 +45,11 @@ Jobs::Jobs(Documents &documents, , m_client(client) , m_queue(documents, projectParts) { - m_queue.setIsJobRunningHandler([this](const Utf8String &translationUnitId) { - return isJobRunning(translationUnitId); + m_queue.setIsJobRunningForTranslationUnitHandler([this](const Utf8String &translationUnitId) { + return isJobRunningForTranslationUnit(translationUnitId); + }); + m_queue.setIsJobRunningForJobRequestHandler([this](const JobRequest &jobRequest) { + return isJobRunningForJobRequest(jobRequest); }); } @@ -146,12 +149,21 @@ JobRequests Jobs::queue() const return m_queue.queue(); } -bool Jobs::isJobRunning(const Utf8String &translationUnitId) const +bool Jobs::isJobRunningForTranslationUnit(const Utf8String &translationUnitId) const { - const auto hasJobRequest = [translationUnitId](const RunningJob &runningJob) { + const auto hasTranslationUnitId = [translationUnitId](const RunningJob &runningJob) { return runningJob.translationUnitId == translationUnitId; }; + return Utils::anyOf(m_running.values(), hasTranslationUnitId); +} + +bool Jobs::isJobRunningForJobRequest(const JobRequest &jobRequest) const +{ + const auto hasJobRequest = [jobRequest](const RunningJob &runningJob) { + return runningJob.jobRequest == jobRequest; + }; + return Utils::anyOf(m_running.values(), hasJobRequest); } diff --git a/src/tools/clangbackend/ipcsource/clangjobs.h b/src/tools/clangbackend/ipcsource/clangjobs.h index 1faf2e3dbdd..02e8ff249d1 100644 --- a/src/tools/clangbackend/ipcsource/clangjobs.h +++ b/src/tools/clangbackend/ipcsource/clangjobs.h @@ -68,7 +68,8 @@ public: public /*for tests*/: QList runningJobs() const; JobRequests queue() const; - bool isJobRunning(const Utf8String &translationUnitId) const; + bool isJobRunningForTranslationUnit(const Utf8String &translationUnitId) const; + bool isJobRunningForJobRequest(const JobRequest &jobRequest) const; private: JobRequests runJobs(const JobRequests &jobRequest); diff --git a/tests/unit/unittest/clangipcserver-test.cpp b/tests/unit/unittest/clangipcserver-test.cpp index 4ce972e3ed3..bcff6e95327 100644 --- a/tests/unit/unittest/clangipcserver-test.cpp +++ b/tests/unit/unittest/clangipcserver-test.cpp @@ -42,6 +42,7 @@ #include #include +#include #include using testing::Property; @@ -322,6 +323,20 @@ TEST_F(ClangClangCodeModelServer, SupportiveTranslationUnitIsSetupAfterFirstEdit ASSERT_TRUE(isSupportiveTranslationUnitInitialized(filePathA)); } +TEST_F(ClangClangCodeModelServer, DoNotRunDuplicateJobs) +{ + registerProjectAndFile(filePathA, 3); + ASSERT_TRUE(waitUntilAllJobsFinished()); + updateUnsavedContent(filePathA, unsavedContent(filePathAUnsavedVersion2), 1); + ASSERT_TRUE(waitUntilAllJobsFinished()); + ASSERT_TRUE(isSupportiveTranslationUnitInitialized(filePathA)); + updateUnsavedContent(filePathA, unsavedContent(filePathAUnsavedVersion2), 2); + QCoreApplication::processEvents(); // adds + runs a job + updateVisibilty(Utf8String(), Utf8String()); + + updateVisibilty(filePathA, filePathA); // triggers adding + runnings job on next processevents() +} + TEST_F(ClangClangCodeModelServer, OpenDocumentAndEdit) { registerProjectAndFile(filePathA, 4); diff --git a/tests/unit/unittest/clangjobqueue-test.cpp b/tests/unit/unittest/clangjobqueue-test.cpp index d03d68d9a18..85ebfe5a1a8 100644 --- a/tests/unit/unittest/clangjobqueue-test.cpp +++ b/tests/unit/unittest/clangjobqueue-test.cpp @@ -95,6 +95,29 @@ TEST_F(JobQueue, AddJob) ASSERT_THAT(jobQueue.queue().size(), Eq(1)); } +TEST_F(JobQueue, DoNotAddDuplicate) +{ + const JobRequest request = createJobRequest(filePath1, + JobRequest::Type::UpdateDocumentAnnotations); + jobQueue.add(request); + + const bool added = jobQueue.add(request); + + ASSERT_FALSE(added); +} + +TEST_F(JobQueue, DoNotAddDuplicateForWhichAJobIsAlreadyRunning) +{ + jobQueue.setIsJobRunningForJobRequestHandler([](const JobRequest &) { + return true; + }); + + const bool added = jobQueue.add(createJobRequest(filePath1, + JobRequest::Type::UpdateDocumentAnnotations)); + + ASSERT_FALSE(added); +} + TEST_F(JobQueue, ProcessEmpty) { jobQueue.processQueue(); @@ -115,7 +138,7 @@ TEST_F(JobQueue, ProcessSingleJob) TEST_F(JobQueue, ProcessUntilEmpty) { jobQueue.add(createJobRequest(filePath1, JobRequest::Type::UpdateDocumentAnnotations)); - jobQueue.add(createJobRequest(filePath1, JobRequest::Type::UpdateDocumentAnnotations)); + jobQueue.add(createJobRequest(filePath1, JobRequest::Type::CreateInitialDocumentPreamble)); JobRequests jobsToRun; ASSERT_THAT(jobQueue.size(), Eq(2)); @@ -235,7 +258,7 @@ TEST_F(JobQueue, PrioritizeCurrentDocumentOverVisible) TEST_F(JobQueue, RunNothingForNotCurrentOrVisibleDocument) { jobQueue.add(createJobRequest(filePath1, JobRequest::Type::UpdateDocumentAnnotations)); - jobQueue.add(createJobRequest(filePath1, JobRequest::Type::UpdateDocumentAnnotations)); + jobQueue.add(createJobRequest(filePath1, JobRequest::Type::CreateInitialDocumentPreamble)); documents.setVisibleInEditors({}); documents.setUsedByCurrentEditor(Utf8StringLiteral("aNonExistingFilePath")); @@ -247,7 +270,7 @@ TEST_F(JobQueue, RunNothingForNotCurrentOrVisibleDocument) TEST_F(JobQueue, RunOnlyOneJobPerTranslationUnitIfMultipleAreInQueue) { jobQueue.add(createJobRequest(filePath1, JobRequest::Type::UpdateDocumentAnnotations)); - jobQueue.add(createJobRequest(filePath1, JobRequest::Type::UpdateDocumentAnnotations)); + jobQueue.add(createJobRequest(filePath1, JobRequest::Type::CreateInitialDocumentPreamble)); const JobRequests jobsToRun = jobQueue.processQueue(); @@ -276,9 +299,9 @@ TEST_F(JobQueue, RunJobsForDistinctTranslationUnits) TEST_F(JobQueue, DoNotRunJobForTranslationUnittThatIsBeingProcessed) { jobQueue.add(createJobRequest(filePath1, JobRequest::Type::UpdateDocumentAnnotations)); - jobQueue.add(createJobRequest(filePath1, JobRequest::Type::UpdateDocumentAnnotations)); + jobQueue.add(createJobRequest(filePath1, JobRequest::Type::CreateInitialDocumentPreamble)); JobRequests jobsToRun = jobQueue.processQueue(); - jobQueue.setIsJobRunningHandler([](const Utf8String &) { + jobQueue.setIsJobRunningForTranslationUnitHandler([](const Utf8String &) { return true; }); diff --git a/tests/unit/unittest/clangjobs-test.cpp b/tests/unit/unittest/clangjobs-test.cpp index fffc6f82375..6285fdd017f 100644 --- a/tests/unit/unittest/clangjobs-test.cpp +++ b/tests/unit/unittest/clangjobs-test.cpp @@ -108,7 +108,7 @@ TEST_F(Jobs, IsJobRunning) jobs.add(createJobRequest(filePath1, JobRequest::Type::UpdateDocumentAnnotations)); jobs.process(); - const bool isJobRunning = jobs.isJobRunning(document.translationUnit().id()); + const bool isJobRunning = jobs.isJobRunningForTranslationUnit(document.translationUnit().id()); ASSERT_TRUE(isJobRunning); } From 2817a1b639205f980e910818c3f43275e4fbf788 Mon Sep 17 00:00:00 2001 From: Nikolai Kosjar Date: Mon, 10 Oct 2016 16:06:54 +0200 Subject: [PATCH 13/27] C++: Fix build of cplusplus-update-frontend cplusplus-update-frontend.cpp:1667:22: error: 'qSort' was not declared in this scope Change-Id: I70a7401239765f6338b3c77187766333fc487f95 Reviewed-by: Orgad Shaneh --- .../cplusplus-update-frontend/cplusplus-update-frontend.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tools/cplusplus-update-frontend/cplusplus-update-frontend.cpp b/src/tools/cplusplus-update-frontend/cplusplus-update-frontend.cpp index 16559edc457..e51355826fa 100644 --- a/src/tools/cplusplus-update-frontend/cplusplus-update-frontend.cpp +++ b/src/tools/cplusplus-update-frontend/cplusplus-update-frontend.cpp @@ -44,6 +44,7 @@ #include #include +#include #include #include @@ -1663,7 +1664,7 @@ void generateASTPatternBuilder_h(const QDir &cplusplusDir) } QStringList classesList = classesSet.toList(); - qSort(classesList); + Utils::sort(classesList); foreach (const QString &className, classesList) { const QString methodName = className.left(className.length() - 3); const QString elementName = className.left(className.length() - 7) + QLatin1String("AST"); From 9e67dfbe17e5b9064086ddd8f64649f257984a6a Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Mon, 10 Oct 2016 10:22:06 +0200 Subject: [PATCH 14/27] CMake: Move more code into BuildDirManager Continue to concentrate all the code reading random cmake files in BuildDirManager. Now the task is to clean up the code, make it less dependent on values it should not depend on (kits, etc.), make it handle changes better and finally add another implementation that uses the cmake server mode to extract the data. Change-Id: I533625e376b969b64287bc205bd2e4be7a605306 Reviewed-by: Tim Jenssen --- .../cmakeprojectmanager/builddirmanager.cpp | 138 ++++++++++++++++++ .../cmakeprojectmanager/builddirmanager.h | 6 + .../cmakebuildconfiguration.cpp | 5 + .../cmakebuildconfiguration.h | 2 + .../cmakeprojectmanager/cmakeproject.cpp | 130 +---------------- .../cmakeprojectmanager/cmakeproject.h | 3 - 6 files changed, 154 insertions(+), 130 deletions(-) diff --git a/src/plugins/cmakeprojectmanager/builddirmanager.cpp b/src/plugins/cmakeprojectmanager/builddirmanager.cpp index 2f874da3bde..266b6512bb1 100644 --- a/src/plugins/cmakeprojectmanager/builddirmanager.cpp +++ b/src/plugins/cmakeprojectmanager/builddirmanager.cpp @@ -36,12 +36,16 @@ #include #include #include +#include +#include #include +#include #include #include #include #include #include +#include #include #include @@ -247,6 +251,44 @@ void BuildDirManager::generateProjectTree(CMakeProjectNode *root) m_files.clear(); // Some of the FileNodes in files() were deleted! } +QSet BuildDirManager::updateCodeModel(CppTools::ProjectPartBuilder &ppBuilder) +{ + QSet languages; + ToolChain *tc = ToolChainKitInformation::toolChain(kit(), ToolChain::Language::Cxx); + const Utils::FileName sysroot = SysRootKitInformation::sysRoot(kit()); + + QHash targetDataCache; + foreach (const CMakeBuildTarget &cbt, buildTargets()) { + if (cbt.targetType == UtilityType) + continue; + + // CMake shuffles the include paths that it reports via the CodeBlocks generator + // So remove the toolchain include paths, so that at least those end up in the correct + // place. + QStringList cxxflags = getCXXFlagsFor(cbt, targetDataCache); + QSet tcIncludes; + foreach (const HeaderPath &hp, tc->systemHeaderPaths(cxxflags, sysroot)) + tcIncludes.insert(hp.path()); + QStringList includePaths; + foreach (const QString &i, cbt.includeFiles) { + if (!tcIncludes.contains(i)) + includePaths.append(i); + } + includePaths += buildDirectory().toString(); + ppBuilder.setIncludePaths(includePaths); + ppBuilder.setCFlags(cxxflags); + ppBuilder.setCxxFlags(cxxflags); + ppBuilder.setDefines(cbt.defines); + ppBuilder.setDisplayName(cbt.title); + + const QSet partLanguages + = QSet::fromList(ppBuilder.createProjectPartsForFiles(cbt.files)); + + languages.unite(partLanguages); + } + return languages; +} + void BuildDirManager::parse() { checkConfiguration(); @@ -545,6 +587,102 @@ void BuildDirManager::processCMakeError() }); } +QStringList BuildDirManager::getCXXFlagsFor(const CMakeBuildTarget &buildTarget, + QHash &cache) +{ + // check cache: + auto it = cache.constFind(buildTarget.title); + if (it != cache.constEnd()) + return *it; + + if (extractCXXFlagsFromMake(buildTarget, cache)) + return cache.value(buildTarget.title); + + if (extractCXXFlagsFromNinja(buildTarget, cache)) + return cache.value(buildTarget.title); + + cache.insert(buildTarget.title, QStringList()); + return QStringList(); +} + +bool BuildDirManager::extractCXXFlagsFromMake(const CMakeBuildTarget &buildTarget, + QHash &cache) +{ + QString makeCommand = QDir::fromNativeSeparators(buildTarget.makeCommand); + int startIndex = makeCommand.indexOf('\"'); + int endIndex = makeCommand.indexOf('\"', startIndex + 1); + if (startIndex != -1 && endIndex != -1) { + startIndex += 1; + QString makefile = makeCommand.mid(startIndex, endIndex - startIndex); + int slashIndex = makefile.lastIndexOf('/'); + makefile.truncate(slashIndex); + makefile.append("/CMakeFiles/" + buildTarget.title + ".dir/flags.make"); + QFile file(makefile); + if (file.exists()) { + file.open(QIODevice::ReadOnly | QIODevice::Text); + QTextStream stream(&file); + while (!stream.atEnd()) { + QString line = stream.readLine().trimmed(); + if (line.startsWith("CXX_FLAGS =")) { + // Skip past = + cache.insert(buildTarget.title, + line.mid(11).trimmed().split(' ', QString::SkipEmptyParts)); + return true; + } + } + } + } + return false; +} + +bool BuildDirManager::extractCXXFlagsFromNinja(const CMakeBuildTarget &buildTarget, + QHash &cache) +{ + Q_UNUSED(buildTarget) + if (!cache.isEmpty()) // We fill the cache in one go! + return false; + + // Attempt to find build.ninja file and obtain FLAGS (CXX_FLAGS) from there if no suitable flags.make were + // found + // Get "all" target's working directory + QByteArray ninjaFile; + QString buildNinjaFile = QDir::fromNativeSeparators(buildTargets().at(0).workingDirectory); + buildNinjaFile += "/build.ninja"; + QFile buildNinja(buildNinjaFile); + if (buildNinja.exists()) { + buildNinja.open(QIODevice::ReadOnly | QIODevice::Text); + ninjaFile = buildNinja.readAll(); + buildNinja.close(); + } + + if (ninjaFile.isEmpty()) + return false; + + QTextStream stream(ninjaFile); + bool cxxFound = false; + const QString targetSignature = "# Object build statements for "; + QString currentTarget; + + while (!stream.atEnd()) { + // 1. Look for a block that refers to the current target + // 2. Look for a build rule which invokes CXX_COMPILER + // 3. Return the FLAGS definition + QString line = stream.readLine().trimmed(); + if (line.startsWith('#')) { + if (line.startsWith(targetSignature)) { + int pos = line.lastIndexOf(' '); + currentTarget = line.mid(pos + 1); + } + } else if (!currentTarget.isEmpty() && line.startsWith("build")) { + cxxFound = line.indexOf("CXX_COMPILER") != -1; + } else if (cxxFound && line.startsWith("FLAGS =")) { + // Skip past = + cache.insert(currentTarget, line.mid(7).trimmed().split(' ', QString::SkipEmptyParts)); + } + } + return !cache.isEmpty(); +} + void BuildDirManager::checkConfiguration() { if (m_tempDir) // always throw away changes in the tmpdir! diff --git a/src/plugins/cmakeprojectmanager/builddirmanager.h b/src/plugins/cmakeprojectmanager/builddirmanager.h index 9d1ee2b10ef..ec26d0c3bb0 100644 --- a/src/plugins/cmakeprojectmanager/builddirmanager.h +++ b/src/plugins/cmakeprojectmanager/builddirmanager.h @@ -45,6 +45,7 @@ QT_FORWARD_DECLARE_CLASS(QTemporaryDir); QT_FORWARD_DECLARE_CLASS(QFileSystemWatcher); namespace Core { class IDocument; } +namespace CppTools { class ProjectPartBuilder; } namespace ProjectExplorer { class FileNode; @@ -80,6 +81,7 @@ public: bool persistCMakeState(); void generateProjectTree(CMakeProjectNode *root); + QSet updateCodeModel(CppTools::ProjectPartBuilder &ppBuilder); QList buildTargets() const; CMakeConfig parsedConfiguration() const; @@ -117,6 +119,10 @@ private: void processCMakeOutput(); void processCMakeError(); + QStringList getCXXFlagsFor(const CMakeBuildTarget &buildTarget, QHash &cache); + bool extractCXXFlagsFromMake(const CMakeBuildTarget &buildTarget, QHash &cache); + bool extractCXXFlagsFromNinja(const CMakeBuildTarget &buildTarget, QHash &cache); + bool m_hasData = false; CMakeBuildConfiguration *m_buildConfiguration = nullptr; diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp index 4291eec4de7..6cdde4147c5 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp @@ -211,6 +211,11 @@ void CMakeBuildConfiguration::generateProjectTree(CMakeProjectNode *root) const return m_buildDirManager->generateProjectTree(root); } +QSet CMakeBuildConfiguration::updateCodeModel(CppTools::ProjectPartBuilder &ppBuilder) +{ + return m_buildDirManager->updateCodeModel(ppBuilder); +} + FileName CMakeBuildConfiguration::shadowBuildDirectory(const FileName &projectFilePath, const Kit *k, const QString &bcName, diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h index ea98c7b1ba9..2fa82cb7d53 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h +++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h @@ -32,6 +32,7 @@ #include #include +namespace CppTools { class ProjectPartBuilder; } namespace ProjectExplorer { class ToolChain; } namespace CMakeProjectManager { @@ -82,6 +83,7 @@ public: QList buildTargets() const; void generateProjectTree(CMakeProjectNode *root) const; + QSet updateCodeModel(CppTools::ProjectPartBuilder &ppBuilder); static Utils::FileName shadowBuildDirectory(const Utils::FileName &projectFilePath, const ProjectExplorer::Kit *k, diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.cpp b/src/plugins/cmakeprojectmanager/cmakeproject.cpp index 0737666457a..50dd058368f 100644 --- a/src/plugins/cmakeprojectmanager/cmakeproject.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeproject.cpp @@ -94,102 +94,6 @@ CMakeProject::~CMakeProject() qDeleteAll(m_extraCompilers); } -QStringList CMakeProject::getCXXFlagsFor(const CMakeBuildTarget &buildTarget, - QHash &cache) -{ - // check cache: - auto it = cache.constFind(buildTarget.title); - if (it != cache.constEnd()) - return *it; - - if (extractCXXFlagsFromMake(buildTarget, cache)) - return cache.value(buildTarget.title); - - if (extractCXXFlagsFromNinja(buildTarget, cache)) - return cache.value(buildTarget.title); - - cache.insert(buildTarget.title, QStringList()); - return QStringList(); -} - -bool CMakeProject::extractCXXFlagsFromMake(const CMakeBuildTarget &buildTarget, - QHash &cache) -{ - QString makeCommand = QDir::fromNativeSeparators(buildTarget.makeCommand); - int startIndex = makeCommand.indexOf('\"'); - int endIndex = makeCommand.indexOf('\"', startIndex + 1); - if (startIndex != -1 && endIndex != -1) { - startIndex += 1; - QString makefile = makeCommand.mid(startIndex, endIndex - startIndex); - int slashIndex = makefile.lastIndexOf('/'); - makefile.truncate(slashIndex); - makefile.append("/CMakeFiles/" + buildTarget.title + ".dir/flags.make"); - QFile file(makefile); - if (file.exists()) { - file.open(QIODevice::ReadOnly | QIODevice::Text); - QTextStream stream(&file); - while (!stream.atEnd()) { - QString line = stream.readLine().trimmed(); - if (line.startsWith("CXX_FLAGS =")) { - // Skip past = - cache.insert(buildTarget.title, - line.mid(11).trimmed().split(' ', QString::SkipEmptyParts)); - return true; - } - } - } - } - return false; -} - -bool CMakeProject::extractCXXFlagsFromNinja(const CMakeBuildTarget &buildTarget, - QHash &cache) -{ - Q_UNUSED(buildTarget) - if (!cache.isEmpty()) // We fill the cache in one go! - return false; - - // Attempt to find build.ninja file and obtain FLAGS (CXX_FLAGS) from there if no suitable flags.make were - // found - // Get "all" target's working directory - QByteArray ninjaFile; - QString buildNinjaFile = QDir::fromNativeSeparators(buildTargets().at(0).workingDirectory); - buildNinjaFile += "/build.ninja"; - QFile buildNinja(buildNinjaFile); - if (buildNinja.exists()) { - buildNinja.open(QIODevice::ReadOnly | QIODevice::Text); - ninjaFile = buildNinja.readAll(); - buildNinja.close(); - } - - if (ninjaFile.isEmpty()) - return false; - - QTextStream stream(ninjaFile); - bool cxxFound = false; - const QString targetSignature = "# Object build statements for "; - QString currentTarget; - - while (!stream.atEnd()) { - // 1. Look for a block that refers to the current target - // 2. Look for a build rule which invokes CXX_COMPILER - // 3. Return the FLAGS definition - QString line = stream.readLine().trimmed(); - if (line.startsWith('#')) { - if (line.startsWith(targetSignature)) { - int pos = line.lastIndexOf(' '); - currentTarget = line.mid(pos + 1); - } - } else if (!currentTarget.isEmpty() && line.startsWith("build")) { - cxxFound = line.indexOf("CXX_COMPILER") != -1; - } else if (cxxFound && line.startsWith("FLAGS =")) { - // Skip past = - cache.insert(currentTarget, line.mid(7).trimmed().split(' ', QString::SkipEmptyParts)); - } - } - return !cache.isEmpty(); -} - void CMakeProject::updateProjectData() { auto cmakeBc = qobject_cast(sender()); @@ -225,39 +129,11 @@ void CMakeProject::updateProjectData() activeQtVersion = CppTools::ProjectPart::Qt5; } - const FileName sysroot = SysRootKitInformation::sysRoot(k); - ppBuilder.setQtVersion(activeQtVersion); - QHash targetDataCache; - foreach (const CMakeBuildTarget &cbt, buildTargets()) { - if (cbt.targetType == UtilityType) - continue; - - // CMake shuffles the include paths that it reports via the CodeBlocks generator - // So remove the toolchain include paths, so that at least those end up in the correct - // place. - QStringList cxxflags = getCXXFlagsFor(cbt, targetDataCache); - QSet tcIncludes; - foreach (const HeaderPath &hp, tc->systemHeaderPaths(cxxflags, sysroot)) { - tcIncludes.insert(hp.path()); - } - QStringList includePaths; - foreach (const QString &i, cbt.includeFiles) { - if (!tcIncludes.contains(i)) - includePaths.append(i); - } - includePaths += projectDirectory().toString(); - ppBuilder.setIncludePaths(includePaths); - ppBuilder.setCFlags(cxxflags); - ppBuilder.setCxxFlags(cxxflags); - ppBuilder.setDefines(cbt.defines); - ppBuilder.setDisplayName(cbt.title); - - const QList languages = ppBuilder.createProjectPartsForFiles(cbt.files); - foreach (Core::Id language, languages) - setProjectLanguage(language, true); - } + const QSet languages = cmakeBc->updateCodeModel(ppBuilder); + for (const auto &lid : languages) + setProjectLanguage(lid, true); m_codeModelFuture.cancel(); pinfo.finish(); diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.h b/src/plugins/cmakeprojectmanager/cmakeproject.h index 90435ff66bd..8ede082cd7f 100644 --- a/src/plugins/cmakeprojectmanager/cmakeproject.h +++ b/src/plugins/cmakeprojectmanager/cmakeproject.h @@ -120,9 +120,6 @@ private: QStringList filesGeneratedFrom(const QString &sourceFile) const final; void updateTargetRunConfigurations(ProjectExplorer::Target *t); void updateApplicationAndDeploymentTargets(); - QStringList getCXXFlagsFor(const CMakeBuildTarget &buildTarget, QHash &cache); - bool extractCXXFlagsFromMake(const CMakeBuildTarget &buildTarget, QHash &cache); - bool extractCXXFlagsFromNinja(const CMakeBuildTarget &buildTarget, QHash &cache); ProjectExplorer::Target *m_connectedTarget = nullptr; From 76b76f2723d9709133ce9a7be057b092609096ba Mon Sep 17 00:00:00 2001 From: Alexander Drozdov Date: Mon, 10 Oct 2016 09:49:46 +1000 Subject: [PATCH 15/27] CMakeProjectManager: Support drop down selector for options CMake provides "hack" for cmake-gui, that allows set options variants and select then from drop down list. Allows Qt Creator re-use this solution. See: - https://blog.kitware.com/constraining-values-with-comboboxes-in-cmake-cmake-gui/ - http://blog.bethcodes.com/cmake-tips-tricks-drop-down-list Drop down values can be added to option via: SET_PROPERTY(CACHE OptionName PROPERTY STRINGS Option1 Option2 Option3) This solution should not restrict to provide any other value, it provides only suggestion for user to select one of prdefined values. Change-Id: I8fc52155775f1e04979db8206bb42363df9359e8 Reviewed-by: Tobias Hunger --- .../cmakeprojectmanager/builddirmanager.cpp | 10 +++ .../cmakebuildconfiguration.cpp | 2 + .../cmakebuildsettingswidget.cpp | 2 + .../cmakeprojectmanager/cmakeconfigitem.cpp | 2 +- .../cmakeprojectmanager/cmakeconfigitem.h | 1 + .../cmakeprojectmanager.pro | 6 +- .../cmakeprojectmanager.qbs | 4 +- .../cmakeprojectmanager/configmodel.cpp | 17 ++-- src/plugins/cmakeprojectmanager/configmodel.h | 9 ++- .../configmodelitemdelegate.cpp | 78 +++++++++++++++++++ .../configmodelitemdelegate.h | 37 +++++++++ 11 files changed, 158 insertions(+), 10 deletions(-) create mode 100644 src/plugins/cmakeprojectmanager/configmodelitemdelegate.cpp create mode 100644 src/plugins/cmakeprojectmanager/configmodelitemdelegate.h diff --git a/src/plugins/cmakeprojectmanager/builddirmanager.cpp b/src/plugins/cmakeprojectmanager/builddirmanager.cpp index 266b6512bb1..5dfcf666fe7 100644 --- a/src/plugins/cmakeprojectmanager/builddirmanager.cpp +++ b/src/plugins/cmakeprojectmanager/builddirmanager.cpp @@ -794,6 +794,7 @@ CMakeConfig BuildDirManager::parseConfiguration(const Utils::FileName &cacheFile } QSet advancedSet; + QMap valuesMap; QByteArray documentation; while (!cache.atEnd()) { const QByteArray line = trimCMakeCacheLine(cache.readLine()); @@ -817,6 +818,8 @@ CMakeConfig BuildDirManager::parseConfiguration(const Utils::FileName &cacheFile if (key.endsWith("-ADVANCED") && value == "1") { advancedSet.insert(key.left(key.count() - 9 /* "-ADVANCED" */)); + } else if (key.endsWith("-STRINGS") && fromByteArray(type) == CMakeConfigItem::INTERNAL) { + valuesMap[key.left(key.count() - 8) /* "-STRINGS" */] = value; } else { CMakeConfigItem::Type t = fromByteArray(type); result << CMakeConfigItem(key, t, documentation, value); @@ -827,6 +830,13 @@ CMakeConfig BuildDirManager::parseConfiguration(const Utils::FileName &cacheFile for (int i = 0; i < result.count(); ++i) { CMakeConfigItem &item = result[i]; item.isAdvanced = advancedSet.contains(item.key); + + if (valuesMap.contains(item.key)) { + item.values = CMakeConfigItem::cmakeSplitValue(QString::fromUtf8(valuesMap[item.key])); + } else if (item.key == "CMAKE_BUILD_TYPE") { + // WA for known options + item.values << "" << "Debug" << "Release" << "MinSizeRel" << "RelWithDebInfo"; + } } Utils::sort(result, CMakeConfigItem::sortOperator()); diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp index 6cdde4147c5..c5d6bdafc4b 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp @@ -245,6 +245,7 @@ QList CMakeBuildConfiguration::completeCMakeConfiguration j.key = QString::fromUtf8(i.key); j.value = QString::fromUtf8(i.value); j.description = QString::fromUtf8(i.documentation); + j.values = i.values; j.isAdvanced = i.isAdvanced || i.type == CMakeConfigItem::INTERNAL; switch (i.type) { @@ -280,6 +281,7 @@ void CMakeBuildConfiguration::setCurrentCMakeConfiguration(const QListsetSelectionBehavior(QAbstractItemView::SelectItems); m_configView->setFrameShape(QFrame::NoFrame); m_configView->hideColumn(2); // Hide isAdvanced column + m_configView->setItemDelegate(new ConfigModelItemDelegate(m_configView)); QFrame *findWrapper = Core::ItemViewFind::createSearchableWrapper(m_configView, Core::ItemViewFind::LightColored); findWrapper->setFrameStyle(QFrame::StyledPanel); diff --git a/src/plugins/cmakeprojectmanager/cmakeconfigitem.cpp b/src/plugins/cmakeprojectmanager/cmakeconfigitem.cpp index 6115b318679..ff1011ef819 100644 --- a/src/plugins/cmakeprojectmanager/cmakeconfigitem.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeconfigitem.cpp @@ -42,7 +42,7 @@ CMakeConfigItem::CMakeConfigItem() = default; CMakeConfigItem::CMakeConfigItem(const CMakeConfigItem &other) : key(other.key), type(other.type), isAdvanced(other.isAdvanced), - value(other.value), documentation(other.documentation) + value(other.value), documentation(other.documentation), values(other.values) { } CMakeConfigItem::CMakeConfigItem(const QByteArray &k, Type t, diff --git a/src/plugins/cmakeprojectmanager/cmakeconfigitem.h b/src/plugins/cmakeprojectmanager/cmakeconfigitem.h index b800a547a21..1484cffda3e 100644 --- a/src/plugins/cmakeprojectmanager/cmakeconfigitem.h +++ b/src/plugins/cmakeprojectmanager/cmakeconfigitem.h @@ -61,6 +61,7 @@ public: bool isAdvanced = false; QByteArray value; // converted to string as needed QByteArray documentation; + QStringList values; }; using CMakeConfig = QList; diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.pro b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.pro index 4621c40c15d..82cd93df82a 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.pro +++ b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.pro @@ -28,7 +28,8 @@ HEADERS = builddirmanager.h \ cmakebuildsettingswidget.h \ cmakeindenter.h \ cmakeautocompleter.h \ - configmodel.h + configmodel.h \ + configmodelitemdelegate.h SOURCES = builddirmanager.cpp \ cmakebuildstep.cpp \ @@ -54,6 +55,7 @@ SOURCES = builddirmanager.cpp \ cmakebuildsettingswidget.cpp \ cmakeindenter.cpp \ cmakeautocompleter.cpp \ - configmodel.cpp + configmodel.cpp \ + configmodelitemdelegate.cpp RESOURCES += cmakeproject.qrc diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs index b199483eaa0..99fe635b277 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs +++ b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs @@ -71,6 +71,8 @@ QtcPlugin { "cmakeautocompleter.h", "cmakeautocompleter.cpp", "configmodel.cpp", - "configmodel.h" + "configmodel.h", + "configmodelitemdelegate.cpp", + "configmodelitemdelegate.h" ] } diff --git a/src/plugins/cmakeprojectmanager/configmodel.cpp b/src/plugins/cmakeprojectmanager/configmodel.cpp index 292cd933bf5..4ef34dd7fab 100644 --- a/src/plugins/cmakeprojectmanager/configmodel.cpp +++ b/src/plugins/cmakeprojectmanager/configmodel.cpp @@ -88,6 +88,15 @@ QVariant ConfigModel::data(const QModelIndex &index, int role) const const InternalDataItem &item = m_configuration[index.row()]; + if (index.column() < 2) { + switch (role) { + case ItemTypeRole: + return item.type; + case ItemValuesRole: + return item.values; + } + } + switch (index.column()) { case 0: switch (role) { @@ -97,8 +106,6 @@ QVariant ConfigModel::data(const QModelIndex &index, int role) const return item.key; case Qt::ToolTipRole: return item.description; - case Qt::UserRole: - return item.type; case Qt::FontRole: { QFont font; font.setItalic(item.isCMakeChanged); @@ -126,8 +133,6 @@ QVariant ConfigModel::data(const QModelIndex &index, int role) const } case Qt::ToolTipRole: return item.description; - case Qt::UserRole: - return item.type; default: return QVariant(); } @@ -209,13 +214,15 @@ QVariant ConfigModel::headerData(int section, Qt::Orientation orientation, int r void ConfigModel::appendConfiguration(const QString &key, const QString &value, const ConfigModel::DataItem::Type type, - const QString &description) + const QString &description, + const QStringList &values) { DataItem item; item.key = key; item.type = type; item.value = value; item.description = description; + item.values = values; InternalDataItem internalItem(item); internalItem.isUserNew = true; diff --git a/src/plugins/cmakeprojectmanager/configmodel.h b/src/plugins/cmakeprojectmanager/configmodel.h index 5fe63baa0fe..05424817188 100644 --- a/src/plugins/cmakeprojectmanager/configmodel.h +++ b/src/plugins/cmakeprojectmanager/configmodel.h @@ -34,6 +34,11 @@ class ConfigModel : public QAbstractTableModel Q_OBJECT public: + enum Roles { + ItemTypeRole = Qt::UserRole, + ItemValuesRole + }; + class DataItem { public: enum Type { BOOLEAN, FILE, DIRECTORY, STRING, UNKNOWN}; @@ -43,6 +48,7 @@ public: bool isAdvanced = false; QString value; QString description; + QStringList values; }; explicit ConfigModel(QObject *parent = nullptr); @@ -58,7 +64,8 @@ public: void appendConfiguration(const QString &key, const QString &value = QString(), const DataItem::Type type = DataItem::UNKNOWN, - const QString &description = QString()); + const QString &description = QString(), + const QStringList &values = QStringList()); void setConfiguration(const QList &config); void flush(); void resetAllChanges(); diff --git a/src/plugins/cmakeprojectmanager/configmodelitemdelegate.cpp b/src/plugins/cmakeprojectmanager/configmodelitemdelegate.cpp new file mode 100644 index 00000000000..b2b19b837e2 --- /dev/null +++ b/src/plugins/cmakeprojectmanager/configmodelitemdelegate.cpp @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Alexander Drozdov. +** Contact: adrozdoff@gmail.com +** +** This file is part of CMakeProjectManager2 plugin. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +****************************************************************************/ + +#include "configmodelitemdelegate.h" +#include "configmodel.h" + +#include + +namespace CMakeProjectManager { + +ConfigModelItemDelegate::ConfigModelItemDelegate(QObject* parent) + : QStyledItemDelegate(parent) +{ } + +ConfigModelItemDelegate::~ConfigModelItemDelegate() +{ } + +QWidget* ConfigModelItemDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + // ComboBox ony in column 2 + if (index.column() != 1) + return QStyledItemDelegate::createEditor(parent, option, index); + + auto model = index.model(); + auto values = model->data(index, ConfigModel::ItemValuesRole).toStringList(); + if (values.isEmpty()) + return QStyledItemDelegate::createEditor(parent, option, index); + + // Create the combobox and populate it + auto cb = new QComboBox(parent); + cb->addItems(values); + cb->setEditable(true); + + return cb; +} + +void ConfigModelItemDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const +{ + if (QComboBox* cb = qobject_cast(editor)) { + // get the index of the text in the combobox that matches the current value of the itenm + QString currentText = index.data(Qt::EditRole).toString(); + int cbIndex = cb->findText(currentText); + // if it is valid, adjust the combobox + if (cbIndex >= 0) + cb->setCurrentIndex(cbIndex); + else + cb->setEditText(currentText); + } else { + QStyledItemDelegate::setEditorData(editor, index); + } +} + +void ConfigModelItemDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const +{ + if (QComboBox* cb = qobject_cast(editor)) + // save the current text of the combo box as the current value of the item + model->setData(index, cb->currentText(), Qt::EditRole); + else + QStyledItemDelegate::setModelData(editor, model, index); +} + +} // namespace CMakeProjectManager + diff --git a/src/plugins/cmakeprojectmanager/configmodelitemdelegate.h b/src/plugins/cmakeprojectmanager/configmodelitemdelegate.h new file mode 100644 index 00000000000..8c20f7b8313 --- /dev/null +++ b/src/plugins/cmakeprojectmanager/configmodelitemdelegate.h @@ -0,0 +1,37 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Alexander Drozdov. +** Contact: adrozdoff@gmail.com +** +** This file is part of CMakeProjectManager2 plugin. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +****************************************************************************/ + +#pragma once + +#include + +namespace CMakeProjectManager { + +class ConfigModelItemDelegate : public QStyledItemDelegate +{ + Q_OBJECT +public: + ConfigModelItemDelegate(QObject* parent=0); + ~ConfigModelItemDelegate(); + + QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override; + void setEditorData(QWidget* editor, const QModelIndex& index) const override; + void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const override; +}; + +} // namespace CMakeProjectManager From 64e92b1ea00599e6e06e884b52cf81319112eaa4 Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Mon, 10 Oct 2016 16:28:09 +0100 Subject: [PATCH 16/27] QMake project manager: add fallthrough comment Change-Id: Icdac22ec69635675fbcd970fcc6215484bdecacd Reviewed-by: Tobias Hunger --- src/plugins/qmakeprojectmanager/wizards/qtprojectparameters.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/qmakeprojectmanager/wizards/qtprojectparameters.cpp b/src/plugins/qmakeprojectmanager/wizards/qtprojectparameters.cpp index 14288807a89..ed71f55b5ab 100644 --- a/src/plugins/qmakeprojectmanager/wizards/qtprojectparameters.cpp +++ b/src/plugins/qmakeprojectmanager/wizards/qtprojectparameters.cpp @@ -93,6 +93,7 @@ void QtProjectParameters::writeProFile(QTextStream &str) const case ConsoleApp: // Mac: Command line apps should not be bundles str << "CONFIG += console\nCONFIG -= app_bundle\n\n"; + // fallthrough case GuiApp: str << "TEMPLATE = app\n"; break; From 52aca31b48d35171f7102b752fb4d20ff6e4cbac Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Mon, 10 Oct 2016 16:23:43 +0100 Subject: [PATCH 17/27] Remote help: switch cplusplus.com to cppreference.com The previous link didn't even work. Change-Id: I876ae6e4ae25ee0d3a842020e0c2cdaa2e2a3446 Reviewed-by: Tobias Hunger --- src/plugins/help/remotehelpfilter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/help/remotehelpfilter.cpp b/src/plugins/help/remotehelpfilter.cpp index 74377a3bb4b..8c5a2add6a4 100644 --- a/src/plugins/help/remotehelpfilter.cpp +++ b/src/plugins/help/remotehelpfilter.cpp @@ -93,7 +93,7 @@ RemoteHelpFilter::RemoteHelpFilter() m_remoteUrls.append("https://www.bing.com/search?q=%1"); m_remoteUrls.append("https://www.google.com/search?q=%1"); m_remoteUrls.append("https://search.yahoo.com/search?p=%1"); - m_remoteUrls.append("https://www.cplusplus.com/reference/stl/%1"); + m_remoteUrls.append("http://en.cppreference.com/mwiki/index.php?title=Special%3ASearch&search=%1"); m_remoteUrls.append("https://en.wikipedia.org/w/index.php?search=%1"); } From ef5e762ec511aa3191b4984d46f0c5fddc1a573d Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Mon, 10 Oct 2016 16:26:29 +0100 Subject: [PATCH 18/27] Remote help: add Stack Overflow Change-Id: Iaa782ab2e7d6d7b36cccfc8291a220fdfa73d19c Reviewed-by: Tobias Hunger --- src/plugins/help/remotehelpfilter.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/help/remotehelpfilter.cpp b/src/plugins/help/remotehelpfilter.cpp index 8c5a2add6a4..faa810e67e8 100644 --- a/src/plugins/help/remotehelpfilter.cpp +++ b/src/plugins/help/remotehelpfilter.cpp @@ -93,6 +93,7 @@ RemoteHelpFilter::RemoteHelpFilter() m_remoteUrls.append("https://www.bing.com/search?q=%1"); m_remoteUrls.append("https://www.google.com/search?q=%1"); m_remoteUrls.append("https://search.yahoo.com/search?p=%1"); + m_remoteUrls.append("https://stackoverflow.com/search?q=%1"); m_remoteUrls.append("http://en.cppreference.com/mwiki/index.php?title=Special%3ASearch&search=%1"); m_remoteUrls.append("https://en.wikipedia.org/w/index.php?search=%1"); } From 2bff4ba1ec00b1a3826d98c45b7b2f32418f5a39 Mon Sep 17 00:00:00 2001 From: Nikolai Kosjar Date: Wed, 5 Oct 2016 16:22:16 +0200 Subject: [PATCH 19/27] Clang: Log clangbackend process errors to "General Messages" pane Change-Id: I687c4a4eb8a7a8874fd88b4d3d6995535dade2c9 Reviewed-by: Leena Miettinen Reviewed-by: David Schulz --- .../clangbackendipcintegration.cpp | 58 +++++++++++++++++-- .../clangbackendipcintegration.h | 6 ++ 2 files changed, 59 insertions(+), 5 deletions(-) diff --git a/src/plugins/clangcodemodel/clangbackendipcintegration.cpp b/src/plugins/clangcodemodel/clangbackendipcintegration.cpp index 9d2962cbef7..1edce8fbd43 100644 --- a/src/plugins/clangcodemodel/clangbackendipcintegration.cpp +++ b/src/plugins/clangcodemodel/clangbackendipcintegration.cpp @@ -32,6 +32,7 @@ #include #include +#include #include #include @@ -68,6 +69,7 @@ #include +#include #include #include #include @@ -304,10 +306,16 @@ public: void updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &) override {} }; +enum { backEndStartTimeOutInMs = 10000 }; + IpcCommunicator::IpcCommunicator() : m_connection(&m_ipcReceiver) , m_ipcSender(new DummyIpcSender) { + m_backendStartTimeOut.setSingleShot(true); + connect(&m_backendStartTimeOut, &QTimer::timeout, + this, &IpcCommunicator::logStartTimeOut); + m_ipcReceiver.setAliveHandler([this]() { m_connection.resetProcessAliveTimer(); }); connect(Core::EditorManager::instance(), &Core::EditorManager::editorAboutToClose, @@ -326,8 +334,11 @@ IpcCommunicator::~IpcCommunicator() void IpcCommunicator::initializeBackend() { const QString clangBackEndProcessPath = backendProcessPath(); + if (!QFileInfo(clangBackEndProcessPath).exists()) { + logExecutableDoesNotExist(); + return; + } qCDebug(log) << "Starting" << clangBackEndProcessPath; - QTC_ASSERT(QFileInfo(clangBackEndProcessPath).exists(), return); m_connection.setProcessAliveTimerInterval(30 * 1000); m_connection.setProcessPath(clangBackEndProcessPath); @@ -338,6 +349,7 @@ void IpcCommunicator::initializeBackend() this, &IpcCommunicator::setupDummySender); m_connection.startProcessAndConnectToServerAsynchronously(); + m_backendStartTimeOut.start(backEndStartTimeOutInMs); } static QStringList projectPartOptions(const CppTools::ProjectPart::Ptr &projectPart) @@ -622,11 +634,11 @@ void IpcCommunicator::updateUnsavedFile(Core::IDocument *document) void IpcCommunicator::onConnectedToBackend() { + m_backendStartTimeOut.stop(); + ++m_connectedCount; - if (m_connectedCount > 1) { - qWarning("Clang back end finished unexpectedly, restarted."); - qCDebug(log) << "Backend restarted, re-initializing with project data and unsaved files."; - } + if (m_connectedCount > 1) + logRestartedDueToUnexpectedFinish(); m_ipcReceiver.deleteAndClearWaitingAssistProcessors(); m_ipcSender.reset(new IpcSender(m_connection)); @@ -645,6 +657,42 @@ void IpcCommunicator::setupDummySender() m_ipcSender.reset(new DummyIpcSender); } +void IpcCommunicator::logExecutableDoesNotExist() +{ + const QString msg + = tr("Clang Code Model: Error: " + "The clangbackend executable \"%1\" does not exist.") + .arg(QDir::toNativeSeparators(backendProcessPath())); + + logError(msg); +} + +void IpcCommunicator::logStartTimeOut() +{ + const QString msg + = tr("Clang Code Model: Error: " + "The clangbackend executable \"%1\" could not be started (timeout after %2ms).") + .arg(QDir::toNativeSeparators(backendProcessPath())) + .arg(backEndStartTimeOutInMs); + + logError(msg); +} + +void IpcCommunicator::logRestartedDueToUnexpectedFinish() +{ + const QString msg + = tr("Clang Code Model: Error: " + "The clangbackend process has finished unexpectedly and was restarted."); + + logError(msg); +} + +void IpcCommunicator::logError(const QString &text) +{ + Core::MessageManager::write(text, Core::MessageManager::Flash); + qWarning("%s", qPrintable(text)); +} + void IpcCommunicator::initializeBackendWithCurrentData() { registerFallbackProjectPart(); diff --git a/src/plugins/clangcodemodel/clangbackendipcintegration.h b/src/plugins/clangcodemodel/clangbackendipcintegration.h index 891a8957f3c..60b915bfc25 100644 --- a/src/plugins/clangcodemodel/clangbackendipcintegration.h +++ b/src/plugins/clangcodemodel/clangbackendipcintegration.h @@ -171,12 +171,18 @@ private: void onDisconnectedFromBackend(); void onEditorAboutToClose(Core::IEditor *editor); + void logExecutableDoesNotExist(); + void logRestartedDueToUnexpectedFinish(); + void logStartTimeOut(); + void logError(const QString &text); + void updateTranslationUnitVisiblity(const Utf8String ¤tEditorFilePath, const Utf8StringVector &visibleEditorsFilePaths); private: IpcReceiver m_ipcReceiver; ClangBackEnd::ClangCodeModelConnectionClient m_connection; + QTimer m_backendStartTimeOut; QScopedPointer m_ipcSender; int m_connectedCount = 0; }; From eb55c1a4e4dfd879ff688489948aa40454007c93 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 5 Sep 2016 18:33:37 +0200 Subject: [PATCH 20/27] Clang: Fix highlightingmarks include test Change-Id: I3e3f34a7155149a4a12fb8cea3102aba10bb5fc7 Reviewed-by: Nikolai Kosjar --- tests/unit/unittest/highlightingmarks-test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/unittest/highlightingmarks-test.cpp b/tests/unit/unittest/highlightingmarks-test.cpp index 577fa66fdd5..1d3ba1a05ec 100644 --- a/tests/unit/unittest/highlightingmarks-test.cpp +++ b/tests/unit/unittest/highlightingmarks-test.cpp @@ -107,7 +107,7 @@ struct Data { Utf8String filePath{Utf8StringLiteral(TESTDATA_DIR"/highlightingmarks.cpp")}; Document document{filePath, ProjectPart(Utf8StringLiteral("projectPartId"), - {Utf8StringLiteral("-std=c++14")}), + {Utf8StringLiteral("-std=c++14"), Utf8StringLiteral("-I" TESTDATA_DIR)}), {}, documents}; TranslationUnit translationUnit{filePath, From a9e0c9e57dfeaab6e53a3ac9945fee4ef7556f83 Mon Sep 17 00:00:00 2001 From: Nikolai Kosjar Date: Wed, 5 Oct 2016 13:39:17 +0200 Subject: [PATCH 21/27] Core: Allow showing/hiding details widget in info bar There might be multiple InfoBarDisplay instances, so create the details widget for each separately. Also, remember the "Show Details" state. Change-Id: I6ee982a9a04373c35d3d1106bf049c0346c50525 Reviewed-by: David Schulz --- src/plugins/coreplugin/infobar.cpp | 40 +++++++++++++++++++++++++++--- src/plugins/coreplugin/infobar.h | 13 +++++++--- 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/src/plugins/coreplugin/infobar.cpp b/src/plugins/coreplugin/infobar.cpp index 405681c5809..86c12ec44f5 100644 --- a/src/plugins/coreplugin/infobar.cpp +++ b/src/plugins/coreplugin/infobar.cpp @@ -32,6 +32,7 @@ #include #include +#include #include #include @@ -67,6 +68,10 @@ void InfoBarEntry::setCancelButtonInfo(const QString &_cancelButtonText, CallBac m_cancelButtonCallBack = callBack; } +void InfoBarEntry::setDetailsWidgetCreator(const InfoBarEntry::DetailsWidgetCreator &creator) +{ + m_detailsWidgetCreator = creator; +} void InfoBar::addInfo(const InfoBarEntry &info) { @@ -151,9 +156,6 @@ bool InfoBar::anyGloballySuppressed() InfoBarDisplay::InfoBarDisplay(QObject *parent) : QObject(parent) - , m_infoBar(0) - , m_boxLayout(0) - , m_boxIndex(0) { } @@ -209,13 +211,43 @@ void InfoBarDisplay::update() infoWidget->setLineWidth(1); infoWidget->setAutoFillBackground(true); - QHBoxLayout *hbox = new QHBoxLayout(infoWidget); + QHBoxLayout *hbox = new QHBoxLayout; hbox->setMargin(2); + auto *vbox = new QVBoxLayout(infoWidget); + vbox->setMargin(0); + vbox->addLayout(hbox); + QLabel *infoWidgetLabel = new QLabel(info.infoText); infoWidgetLabel->setWordWrap(true); hbox->addWidget(infoWidgetLabel); + if (info.m_detailsWidgetCreator) { + if (m_isShowingDetailsWidget) { + QWidget *detailsWidget = info.m_detailsWidgetCreator(); + vbox->addWidget(detailsWidget); + } + + auto *showDetailsButton = new QToolButton; + showDetailsButton->setCheckable(true); + showDetailsButton->setChecked(m_isShowingDetailsWidget); + showDetailsButton->setText(tr("&Show Details")); + connect(showDetailsButton, &QToolButton::clicked, [this, vbox, info] (bool) { + QWidget *detailsWidget = vbox->count() == 2 ? vbox->itemAt(1)->widget() : nullptr; + if (!detailsWidget) { + detailsWidget = info.m_detailsWidgetCreator(); + vbox->addWidget(detailsWidget); + } + + m_isShowingDetailsWidget = !m_isShowingDetailsWidget; + detailsWidget->setVisible(m_isShowingDetailsWidget); + }); + + hbox->addWidget(showDetailsButton); + } else { + m_isShowingDetailsWidget = false; + } + if (!info.buttonText.isEmpty()) { QToolButton *infoWidgetButton = new QToolButton; infoWidgetButton->setText(info.buttonText); diff --git a/src/plugins/coreplugin/infobar.h b/src/plugins/coreplugin/infobar.h index 8c2cc7f211d..da0f8596447 100644 --- a/src/plugins/coreplugin/infobar.h +++ b/src/plugins/coreplugin/infobar.h @@ -54,11 +54,14 @@ public: InfoBarEntry(Id _id, const QString &_infoText, GlobalSuppressionMode _globalSuppression = GlobalSuppressionDisabled); InfoBarEntry(const InfoBarEntry &other) { *this = other; } - typedef std::function CallBack; + using CallBack = std::function; void setCustomButtonInfo(const QString &_buttonText, CallBack callBack); void setCancelButtonInfo(CallBack callBack); void setCancelButtonInfo(const QString &_cancelButtonText, CallBack callBack); + using DetailsWidgetCreator = std::function; + void setDetailsWidgetCreator(const DetailsWidgetCreator &creator); + private: Id id; QString infoText; @@ -67,6 +70,7 @@ private: QString cancelButtonText; CallBack m_cancelButtonCallBack; GlobalSuppressionMode globalSuppression; + DetailsWidgetCreator m_detailsWidgetCreator; friend class InfoBar; friend class InfoBarDisplay; }; @@ -113,9 +117,10 @@ private: void widgetDestroyed(); QList m_infoWidgets; - InfoBar *m_infoBar; - QBoxLayout *m_boxLayout; - int m_boxIndex; + InfoBar *m_infoBar = nullptr; + QBoxLayout *m_boxLayout = nullptr; + int m_boxIndex = 0; + bool m_isShowingDetailsWidget = false; }; } // namespace Core From 1cece95f9ac297feeeb1e3eb0a98fc563ece9000 Mon Sep 17 00:00:00 2001 From: Nikolai Kosjar Date: Tue, 11 Oct 2016 10:49:25 +0200 Subject: [PATCH 22/27] Core: Allow disabling default cancel button in info bar Change-Id: I83f7903854823bb156f57f0fe45eb041a5f35044 Reviewed-by: David Schulz --- src/plugins/coreplugin/infobar.cpp | 18 ++++++++++++++---- src/plugins/coreplugin/infobar.h | 2 ++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/plugins/coreplugin/infobar.cpp b/src/plugins/coreplugin/infobar.cpp index 86c12ec44f5..fe4e86516fa 100644 --- a/src/plugins/coreplugin/infobar.cpp +++ b/src/plugins/coreplugin/infobar.cpp @@ -68,6 +68,11 @@ void InfoBarEntry::setCancelButtonInfo(const QString &_cancelButtonText, CallBac m_cancelButtonCallBack = callBack; } +void InfoBarEntry::setShowDefaultCancelButton(bool yesno) +{ + m_showDefaultCancelButton = yesno; +} + void InfoBarEntry::setDetailsWidgetCreator(const InfoBarEntry::DetailsWidgetCreator &creator) { m_detailsWidgetCreator = creator; @@ -277,12 +282,17 @@ void InfoBarDisplay::update() }); if (info.cancelButtonText.isEmpty()) { - infoWidgetCloseButton->setAutoRaise(true); - infoWidgetCloseButton->setIcon(Utils::Icons::CLOSE_FOREGROUND.icon()); - infoWidgetCloseButton->setToolTip(tr("Close")); + if (info.m_showDefaultCancelButton) { + infoWidgetCloseButton->setAutoRaise(true); + infoWidgetCloseButton->setIcon(Utils::Icons::CLOSE_FOREGROUND.icon()); + infoWidgetCloseButton->setToolTip(tr("Close")); + } + if (infoWidgetSuppressButton) hbox->addWidget(infoWidgetSuppressButton); - hbox->addWidget(infoWidgetCloseButton); + + if (info.m_showDefaultCancelButton) + hbox->addWidget(infoWidgetCloseButton); } else { infoWidgetCloseButton->setText(info.cancelButtonText); hbox->addWidget(infoWidgetCloseButton); diff --git a/src/plugins/coreplugin/infobar.h b/src/plugins/coreplugin/infobar.h index da0f8596447..7ba76edc383 100644 --- a/src/plugins/coreplugin/infobar.h +++ b/src/plugins/coreplugin/infobar.h @@ -58,6 +58,7 @@ public: void setCustomButtonInfo(const QString &_buttonText, CallBack callBack); void setCancelButtonInfo(CallBack callBack); void setCancelButtonInfo(const QString &_cancelButtonText, CallBack callBack); + void setShowDefaultCancelButton(bool yesno); using DetailsWidgetCreator = std::function; void setDetailsWidgetCreator(const DetailsWidgetCreator &creator); @@ -71,6 +72,7 @@ private: CallBack m_cancelButtonCallBack; GlobalSuppressionMode globalSuppression; DetailsWidgetCreator m_detailsWidgetCreator; + bool m_showDefaultCancelButton = true; friend class InfoBar; friend class InfoBarDisplay; }; From b5023bda5a1579b5e3625deb6b4893539a2fab6d Mon Sep 17 00:00:00 2001 From: Nikolai Kosjar Date: Tue, 11 Oct 2016 13:08:00 +0200 Subject: [PATCH 23/27] Core: Add suppression call back for info bar Change-Id: I56bc10adcd101ed38c463096f1cf9396f0ff8b1e Reviewed-by: David Schulz --- src/plugins/coreplugin/infobar.cpp | 9 ++++++++- src/plugins/coreplugin/infobar.h | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/plugins/coreplugin/infobar.cpp b/src/plugins/coreplugin/infobar.cpp index fe4e86516fa..acbccd47d39 100644 --- a/src/plugins/coreplugin/infobar.cpp +++ b/src/plugins/coreplugin/infobar.cpp @@ -68,6 +68,11 @@ void InfoBarEntry::setCancelButtonInfo(const QString &_cancelButtonText, CallBac m_cancelButtonCallBack = callBack; } +void InfoBarEntry::setSuppressionButtonInfo(InfoBarEntry::CallBack callback) +{ + m_suppressionButtonCallBack = callback; +} + void InfoBarEntry::setShowDefaultCancelButton(bool yesno) { m_showDefaultCancelButton = yesno; @@ -266,7 +271,9 @@ void InfoBarDisplay::update() if (info.globalSuppression == InfoBarEntry::GlobalSuppressionEnabled) { infoWidgetSuppressButton = new QToolButton; infoWidgetSuppressButton->setText(tr("Do Not Show Again")); - connect(infoWidgetSuppressButton, &QAbstractButton::clicked, this, [this, id] { + connect(infoWidgetSuppressButton, &QAbstractButton::clicked, this, [this, info, id] { + if (info.m_suppressionButtonCallBack) + info.m_suppressionButtonCallBack(); m_infoBar->removeInfo(id); InfoBar::globallySuppressInfo(id); }); diff --git a/src/plugins/coreplugin/infobar.h b/src/plugins/coreplugin/infobar.h index 7ba76edc383..906f74ca78b 100644 --- a/src/plugins/coreplugin/infobar.h +++ b/src/plugins/coreplugin/infobar.h @@ -58,6 +58,7 @@ public: void setCustomButtonInfo(const QString &_buttonText, CallBack callBack); void setCancelButtonInfo(CallBack callBack); void setCancelButtonInfo(const QString &_cancelButtonText, CallBack callBack); + void setSuppressionButtonInfo(CallBack callback); void setShowDefaultCancelButton(bool yesno); using DetailsWidgetCreator = std::function; @@ -70,6 +71,7 @@ private: CallBack m_buttonCallBack; QString cancelButtonText; CallBack m_cancelButtonCallBack; + CallBack m_suppressionButtonCallBack; GlobalSuppressionMode globalSuppression; DetailsWidgetCreator m_detailsWidgetCreator; bool m_showDefaultCancelButton = true; From cb24872f439a92e7ab0d6ed6919c9c253707c3d9 Mon Sep 17 00:00:00 2001 From: Nikolai Kosjar Date: Tue, 11 Oct 2016 13:08:24 +0200 Subject: [PATCH 24/27] Core: Allow to globally unsuppress an info Change-Id: Ie7af913e28a323e4ad391a35cfd65abd084cfd99 Reviewed-by: David Schulz --- src/plugins/coreplugin/infobar.cpp | 19 +++++++++++++++---- src/plugins/coreplugin/infobar.h | 4 ++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/plugins/coreplugin/infobar.cpp b/src/plugins/coreplugin/infobar.cpp index acbccd47d39..9fcc7ce18ae 100644 --- a/src/plugins/coreplugin/infobar.cpp +++ b/src/plugins/coreplugin/infobar.cpp @@ -139,10 +139,13 @@ void InfoBar::clear() void InfoBar::globallySuppressInfo(Id id) { globallySuppressed.insert(id); - QStringList list; - foreach (Id i, globallySuppressed) - list << QLatin1String(i.name()); - ICore::settings()->setValue(QLatin1String(C_SUPPRESSED_WARNINGS), list); + writeGloballySuppressedToSettings(); +} + +void InfoBar::globallyUnsuppressInfo(Id id) +{ + globallySuppressed.remove(id); + writeGloballySuppressedToSettings(); } void InfoBar::initializeGloballySuppressed() @@ -163,6 +166,14 @@ bool InfoBar::anyGloballySuppressed() return !globallySuppressed.isEmpty(); } +void InfoBar::writeGloballySuppressedToSettings() +{ + QStringList list; + foreach (Id i, globallySuppressed) + list << QLatin1String(i.name()); + ICore::settings()->setValue(QLatin1String(C_SUPPRESSED_WARNINGS), list); +} + InfoBarDisplay::InfoBarDisplay(QObject *parent) : QObject(parent) diff --git a/src/plugins/coreplugin/infobar.h b/src/plugins/coreplugin/infobar.h index 906f74ca78b..e832e570055 100644 --- a/src/plugins/coreplugin/infobar.h +++ b/src/plugins/coreplugin/infobar.h @@ -92,6 +92,7 @@ public: void enableInfo(Id id); void clear(); static void globallySuppressInfo(Id id); + static void globallyUnsuppressInfo(Id id); static void initializeGloballySuppressed(); static void clearGloballySuppressed(); static bool anyGloballySuppressed(); @@ -99,6 +100,9 @@ public: signals: void changed(); +private: + static void writeGloballySuppressedToSettings(); + private: QList m_infoBarEntries; QSet m_suppressed; From 9d55d8485cda7077d1280f3335102ffd036240b5 Mon Sep 17 00:00:00 2001 From: Nikolai Kosjar Date: Tue, 4 Oct 2016 16:23:42 +0200 Subject: [PATCH 25/27] Clang: Show info bar for parse errors in header files ...because those errors can lead to a substantial performance/functional regression. The actual diagnostics (possibly with children) are shown as details in the info bar. The info bar can be hidden with the "Do Not Show Again" button. Re-enabling the info bar is possible with the new editor tool bar button. Change-Id: I03394ff8e3c84127946b0b791930b28a385f5a46 Reviewed-by: David Schulz --- .../documentannotationschangedmessage.cpp | 2 + .../documentannotationschangedmessage.h | 11 + .../clangbackendipcintegration.cpp | 4 +- .../clangcodemodel/clangdiagnosticmanager.cpp | 1 + .../clangdiagnostictooltipwidget.cpp | 65 ++++-- .../clangdiagnostictooltipwidget.h | 10 +- .../clangeditordocumentprocessor.cpp | 47 ++++- .../clangeditordocumentprocessor.h | 3 + src/plugins/clangcodemodel/clangtextmark.cpp | 2 +- src/plugins/cppeditor/cppeditor.cpp | 67 +++++- src/plugins/cppeditor/cppeditor.h | 12 ++ src/plugins/cppeditor/cppeditorconstants.h | 1 + src/plugins/cppeditor/cppeditordocument.h | 4 + .../cpptools/baseeditordocumentprocessor.h | 7 + .../builtineditordocumentprocessor.cpp | 5 +- .../clangrequestdocumentannotationsjob.cpp | 4 +- .../clangrequestdocumentannotationsjob.h | 1 + .../ipcsource/clangtranslationunit.cpp | 45 +++- .../ipcsource/clangtranslationunit.h | 6 +- .../clangupdatedocumentannotationsjob.cpp | 4 +- .../clangupdatedocumentannotationsjob.h | 1 + .../unittest/clangtranslationunit-test.cpp | 197 ++++++++++++++++++ .../unittest/clientserverinprocess-test.cpp | 1 + .../readandwritemessageblock-test.cpp | 1 + tests/unit/unittest/unittest.pro | 1 + 25 files changed, 458 insertions(+), 44 deletions(-) create mode 100644 tests/unit/unittest/clangtranslationunit-test.cpp diff --git a/src/libs/clangbackendipc/documentannotationschangedmessage.cpp b/src/libs/clangbackendipc/documentannotationschangedmessage.cpp index b8b4b36601c..6c7ef0d5cb6 100644 --- a/src/libs/clangbackendipc/documentannotationschangedmessage.cpp +++ b/src/libs/clangbackendipc/documentannotationschangedmessage.cpp @@ -37,6 +37,7 @@ QDebug operator<<(QDebug debug, const DocumentAnnotationsChangedMessage &message debug.nospace() << "DocumentAnnotationsChangedMessage(" << message.fileContainer() << ", " << message.diagnostics().size() + << ", " << !message.firstHeaderErrorDiagnostic().text().isEmpty() << ", " << message.highlightingMarks().size() << ", " << message.skippedPreprocessorRanges().size() << ")"; @@ -49,6 +50,7 @@ void PrintTo(const DocumentAnnotationsChangedMessage &message, ::std::ostream* o *os << "DocumentAnnotationsChangedMessage("; PrintTo(message.fileContainer(), os); *os << "," << message.diagnostics().size(); + *os << "," << !message.firstHeaderErrorDiagnostic().text().isEmpty(); *os << "," << message.highlightingMarks().size(); *os << "," << message.skippedPreprocessorRanges().size(); *os << ")"; diff --git a/src/libs/clangbackendipc/documentannotationschangedmessage.h b/src/libs/clangbackendipc/documentannotationschangedmessage.h index 556794385d8..b121fc6ecd8 100644 --- a/src/libs/clangbackendipc/documentannotationschangedmessage.h +++ b/src/libs/clangbackendipc/documentannotationschangedmessage.h @@ -41,10 +41,12 @@ public: DocumentAnnotationsChangedMessage() = default; DocumentAnnotationsChangedMessage(const FileContainer &fileContainer, const QVector &diagnostics, + const DiagnosticContainer &firstHeaderErrorDiagnostic_, const QVector &highlightingMarks, const QVector &skippedPreprocessorRanges) : fileContainer_(fileContainer), diagnostics_(diagnostics), + firstHeaderErrorDiagnostic_(firstHeaderErrorDiagnostic_), highlightingMarks_(highlightingMarks), skippedPreprocessorRanges_(skippedPreprocessorRanges) { @@ -60,6 +62,11 @@ public: return diagnostics_; } + const DiagnosticContainer &firstHeaderErrorDiagnostic() const + { + return firstHeaderErrorDiagnostic_; + } + const QVector &highlightingMarks() const { return highlightingMarks_; @@ -74,6 +81,7 @@ public: { out << message.fileContainer_; out << message.diagnostics_; + out << message.firstHeaderErrorDiagnostic_; out << message.highlightingMarks_; out << message.skippedPreprocessorRanges_; @@ -84,6 +92,7 @@ public: { in >> message.fileContainer_; in >> message.diagnostics_; + in >> message.firstHeaderErrorDiagnostic_; in >> message.highlightingMarks_; in >> message.skippedPreprocessorRanges_; @@ -95,6 +104,7 @@ public: { return first.fileContainer_ == second.fileContainer_ && first.diagnostics_ == second.diagnostics_ + && first.firstHeaderErrorDiagnostic_ == second.firstHeaderErrorDiagnostic_ && first.highlightingMarks_ == second.highlightingMarks_ && first.skippedPreprocessorRanges_ == second.skippedPreprocessorRanges_; } @@ -102,6 +112,7 @@ public: private: FileContainer fileContainer_; QVector diagnostics_; + DiagnosticContainer firstHeaderErrorDiagnostic_; QVector highlightingMarks_; QVector skippedPreprocessorRanges_; }; diff --git a/src/plugins/clangcodemodel/clangbackendipcintegration.cpp b/src/plugins/clangcodemodel/clangbackendipcintegration.cpp index 1edce8fbd43..831a3bdea62 100644 --- a/src/plugins/clangcodemodel/clangbackendipcintegration.cpp +++ b/src/plugins/clangcodemodel/clangbackendipcintegration.cpp @@ -182,7 +182,9 @@ void IpcReceiver::documentAnnotationsChanged(const DocumentAnnotationsChangedMes const QString documentProjectPartId = CppTools::CppToolsBridge::projectPartIdForFile(filePath); if (projectPartId == documentProjectPartId) { const quint32 documentRevision = message.fileContainer().documentRevision(); - processor->updateCodeWarnings(message.diagnostics(), documentRevision); + processor->updateCodeWarnings(message.diagnostics(), + message.firstHeaderErrorDiagnostic(), + documentRevision); processor->updateHighlighting(message.highlightingMarks(), message.skippedPreprocessorRanges(), documentRevision); diff --git a/src/plugins/clangcodemodel/clangdiagnosticmanager.cpp b/src/plugins/clangcodemodel/clangdiagnosticmanager.cpp index 2bfa7baac7d..52c441443d6 100644 --- a/src/plugins/clangcodemodel/clangdiagnosticmanager.cpp +++ b/src/plugins/clangcodemodel/clangdiagnosticmanager.cpp @@ -41,6 +41,7 @@ #include #include +#include #include namespace { diff --git a/src/plugins/clangcodemodel/clangdiagnostictooltipwidget.cpp b/src/plugins/clangcodemodel/clangdiagnostictooltipwidget.cpp index 52c097f305a..5b6c48c9536 100644 --- a/src/plugins/clangcodemodel/clangdiagnostictooltipwidget.cpp +++ b/src/plugins/clangcodemodel/clangdiagnostictooltipwidget.cpp @@ -129,9 +129,10 @@ enum IndentType { IndentDiagnostic, DoNotIndentDiagnostic }; QWidget *createDiagnosticLabel(const ClangBackEnd::DiagnosticContainer &diagnostic, const QString &mainFilePath, - IndentType indentType = DoNotIndentDiagnostic) + IndentType indentType = DoNotIndentDiagnostic, + bool enableClickableFixits = true) { - const bool hasFixit = !diagnostic.fixIts().isEmpty(); + const bool hasFixit = enableClickableFixits ? !diagnostic.fixIts().isEmpty() : false; const QString diagnosticText = diagnostic.text().toString().toHtmlEscaped(); const QString text = clickableLocation(mainFilePath, diagnostic.location()) + QStringLiteral(": ") @@ -159,25 +160,35 @@ class MainDiagnosticWidget : public QWidget { Q_OBJECT public: - MainDiagnosticWidget(const ClangBackEnd::DiagnosticContainer &diagnostic) + MainDiagnosticWidget(const ClangBackEnd::DiagnosticContainer &diagnostic, + const ClangCodeModel::Internal::DisplayHints &displayHints) { setContentsMargins(0, 0, 0, 0); auto *mainLayout = createLayout(); - // Set up header row: category + responsible option - const QString category = diagnostic.category(); - const QString responsibleOption = diagnostic.enableOption(); const ClangBackEnd::SourceLocationContainer location = diagnostic.location(); - auto *headerLayout = createLayout(); - headerLayout->addWidget(new QLabel(wrapInBoldTags(category)), 1); + // Set up header row: category + responsible option + if (displayHints.showMainDiagnosticHeader) { + const QString category = diagnostic.category(); + const QString responsibleOption = diagnostic.enableOption(); - auto *responsibleOptionLabel = new QLabel(wrapInColor(responsibleOption, "gray")); - headerLayout->addWidget(responsibleOptionLabel, 0); - mainLayout->addLayout(headerLayout); + auto *headerLayout = createLayout(); + headerLayout->addWidget(new QLabel(wrapInBoldTags(category)), 1); + + auto *responsibleOptionLabel = new QLabel(wrapInColor(responsibleOption, "gray")); + headerLayout->addWidget(responsibleOptionLabel, 0); + mainLayout->addLayout(headerLayout); + } // Set up main row: diagnostic text - mainLayout->addWidget(createDiagnosticLabel(diagnostic, location.filePath())); + const Utf8String mainFilePath = displayHints.showFileNameInMainDiagnostic + ? Utf8String() + : location.filePath(); + mainLayout->addWidget(createDiagnosticLabel(diagnostic, + mainFilePath, + DoNotIndentDiagnostic, + displayHints.enableClickableFixits)); setLayout(mainLayout); } @@ -186,26 +197,35 @@ public: void addChildrenToLayout(const QString &mainFilePath, const QVector::const_iterator first, const QVector::const_iterator last, + bool enableClickableFixits, QLayout &boxLayout) { - for (auto it = first; it != last; ++it) - boxLayout.addWidget(createDiagnosticLabel(*it, mainFilePath, IndentDiagnostic)); + for (auto it = first; it != last; ++it) { + boxLayout.addWidget(createDiagnosticLabel(*it, + mainFilePath, + IndentDiagnostic, + enableClickableFixits)); + } } void setupChildDiagnostics(const QString &mainFilePath, const QVector &diagnostics, + bool enableClickableFixits, QLayout &boxLayout) { if (diagnostics.size() <= 10) { - addChildrenToLayout(mainFilePath, diagnostics.begin(), diagnostics.end(), boxLayout); + addChildrenToLayout(mainFilePath, diagnostics.begin(), diagnostics.end(), + enableClickableFixits, boxLayout); } else { - addChildrenToLayout(mainFilePath, diagnostics.begin(), diagnostics.begin() + 7, boxLayout); + addChildrenToLayout(mainFilePath, diagnostics.begin(), diagnostics.begin() + 7, + enableClickableFixits, boxLayout); auto ellipsisLabel = new QLabel(QStringLiteral("...")); ellipsisLabel->setContentsMargins(childIndentationOnTheLeftInPixel, 0, 0, 0); boxLayout.addWidget(ellipsisLabel); - addChildrenToLayout(mainFilePath, diagnostics.end() - 3, diagnostics.end(), boxLayout); + addChildrenToLayout(mainFilePath, diagnostics.end() - 3, diagnostics.end(), + enableClickableFixits, boxLayout); } } @@ -214,13 +234,18 @@ void setupChildDiagnostics(const QString &mainFilePath, namespace ClangCodeModel { namespace Internal { -void addToolTipToLayout(const ClangBackEnd::DiagnosticContainer &diagnostic, QLayout *target) +void addToolTipToLayout(const ClangBackEnd::DiagnosticContainer &diagnostic, + QLayout *target, + const DisplayHints &displayHints) { // Set up header and text row for main diagnostic - target->addWidget(new MainDiagnosticWidget(diagnostic)); + target->addWidget(new MainDiagnosticWidget(diagnostic, displayHints)); // Set up child rows for notes - setupChildDiagnostics(diagnostic.location().filePath(), diagnostic.children(), *target); + setupChildDiagnostics(diagnostic.location().filePath(), + diagnostic.children(), + displayHints.enableClickableFixits, + *target); } } // namespace Internal diff --git a/src/plugins/clangcodemodel/clangdiagnostictooltipwidget.h b/src/plugins/clangcodemodel/clangdiagnostictooltipwidget.h index 600ccf22d83..839952fc774 100644 --- a/src/plugins/clangcodemodel/clangdiagnostictooltipwidget.h +++ b/src/plugins/clangcodemodel/clangdiagnostictooltipwidget.h @@ -34,7 +34,15 @@ QT_END_NAMESPACE namespace ClangCodeModel { namespace Internal { -void addToolTipToLayout(const ClangBackEnd::DiagnosticContainer &diagnostic, QLayout *target); +struct DisplayHints { + bool showMainDiagnosticHeader = true; + bool showFileNameInMainDiagnostic = false; + bool enableClickableFixits = true; +}; + +void addToolTipToLayout(const ClangBackEnd::DiagnosticContainer &diagnostic, + QLayout *target, + const DisplayHints &displayHints = DisplayHints()); } // namespace Internal } // namespace ClangCodeModel diff --git a/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp b/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp index 99c6a670339..09a7b76b6f1 100644 --- a/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp +++ b/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp @@ -57,6 +57,8 @@ #include #include +#include +#include namespace ClangCodeModel { namespace Internal { @@ -167,14 +169,21 @@ void ClangEditorDocumentProcessor::clearProjectPart() m_projectPart.clear(); } -void ClangEditorDocumentProcessor::updateCodeWarnings(const QVector &diagnostics, - uint documentRevision) +void ClangEditorDocumentProcessor::updateCodeWarnings( + const QVector &diagnostics, + const ClangBackEnd::DiagnosticContainer &firstHeaderErrorDiagnostic, + uint documentRevision) { if (documentRevision == revision()) { m_diagnosticManager.processNewDiagnostics(diagnostics); const auto codeWarnings = m_diagnosticManager.takeExtraSelections(); const auto fixitAvailableMarkers = m_diagnosticManager.takeFixItAvailableMarkers(); - emit codeWarningsUpdated(revision(), codeWarnings, fixitAvailableMarkers); + const auto creator = creatorForHeaderErrorDiagnosticWidget(firstHeaderErrorDiagnostic); + + emit codeWarningsUpdated(revision(), + codeWarnings, + creator, + fixitAvailableMarkers); } } namespace { @@ -333,6 +342,38 @@ void ClangEditorDocumentProcessor::requestDocumentAnnotations(const QString &pro m_ipcCommunicator.requestDocumentAnnotations(fileContainer); } +static Internal::DisplayHints displayHintsForInfoBar() +{ + Internal::DisplayHints displayHints; + displayHints.showMainDiagnosticHeader = false; + displayHints.showFileNameInMainDiagnostic = true; + displayHints.enableClickableFixits = false; // Tool chain headers might be changed, so disable. + + return displayHints; +} + +CppTools::BaseEditorDocumentProcessor::HeaderErrorDiagnosticWidgetCreator +ClangEditorDocumentProcessor::creatorForHeaderErrorDiagnosticWidget( + const ClangBackEnd::DiagnosticContainer &firstHeaderErrorDiagnostic) +{ + if (firstHeaderErrorDiagnostic.text().isEmpty()) + return CppTools::BaseEditorDocumentProcessor::HeaderErrorDiagnosticWidgetCreator(); + + return [firstHeaderErrorDiagnostic]() { + auto vbox = new QVBoxLayout; + vbox->setMargin(0); + vbox->setContentsMargins(10, 0, 0, 2); + vbox->setSpacing(2); + + addToolTipToLayout(firstHeaderErrorDiagnostic, vbox, displayHintsForInfoBar()); + + auto widget = new QWidget; + widget->setLayout(vbox); + + return widget; + }; +} + static CppTools::ProjectPart projectPartForLanguageOption(CppTools::ProjectPart *projectPart) { if (projectPart) diff --git a/src/plugins/clangcodemodel/clangeditordocumentprocessor.h b/src/plugins/clangcodemodel/clangeditordocumentprocessor.h index a23d93d72fb..10355e349d7 100644 --- a/src/plugins/clangcodemodel/clangeditordocumentprocessor.h +++ b/src/plugins/clangcodemodel/clangeditordocumentprocessor.h @@ -68,6 +68,7 @@ public: void clearProjectPart(); void updateCodeWarnings(const QVector &diagnostics, + const ClangBackEnd::DiagnosticContainer &firstHeaderErrorDiagnostic, uint documentRevision); void updateHighlighting(const QVector &highlightingMarks, const QVector &skippedPreprocessorRanges, @@ -96,6 +97,8 @@ private: void registerTranslationUnitForEditor(CppTools::ProjectPart *projectPart); void updateTranslationUnitIfProjectPartExists(); void requestDocumentAnnotations(const QString &projectpartId); + HeaderErrorDiagnosticWidgetCreator creatorForHeaderErrorDiagnosticWidget( + const ClangBackEnd::DiagnosticContainer &firstHeaderErrorDiagnostic); ClangBackEnd::FileContainer fileContainerWithArguments(CppTools::ProjectPart *projectPart) const; ClangBackEnd::FileContainer fileContainerWithDocumentContent(const QString &projectpartId) const; diff --git a/src/plugins/clangcodemodel/clangtextmark.cpp b/src/plugins/clangcodemodel/clangtextmark.cpp index 137787daa64..8057d66cb61 100644 --- a/src/plugins/clangcodemodel/clangtextmark.cpp +++ b/src/plugins/clangcodemodel/clangtextmark.cpp @@ -84,7 +84,7 @@ void ClangTextMark::setIcon(ClangBackEnd::DiagnosticSeverity severity) bool ClangTextMark::addToolTipContent(QLayout *target) { - Internal::addToolTipToLayout(m_diagnostic, target); + Internal::addToolTipToLayout(m_diagnostic, target, Internal::DisplayHints()); return true; } diff --git a/src/plugins/cppeditor/cppeditor.cpp b/src/plugins/cppeditor/cppeditor.cpp index c26a8b25f1a..0492635d1a6 100644 --- a/src/plugins/cppeditor/cppeditor.cpp +++ b/src/plugins/cppeditor/cppeditor.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include @@ -77,6 +78,7 @@ #include #include #include +#include #include #include @@ -126,9 +128,13 @@ public: QSharedPointer m_declDefLink; QScopedPointer m_followSymbolUnderCursor; - QToolButton *m_preprocessorButton; + + QToolButton *m_preprocessorButton = nullptr; + QToolButton *m_headerErrorsIndicatorButton = nullptr; CppSelectionChanger m_cppSelectionChanger; + + CppEditorWidget::HeaderErrorDiagnosticWidgetCreator m_headerErrorDiagnosticWidgetCreator; }; CppEditorWidgetPrivate::CppEditorWidgetPrivate(CppEditorWidget *q) @@ -139,7 +145,6 @@ CppEditorWidgetPrivate::CppEditorWidgetPrivate(CppEditorWidget *q) , m_useSelectionsUpdater(q) , m_declDefLinkFinder(new FunctionDeclDefLinkFinder(q)) , m_followSymbolUnderCursor(new FollowSymbolUnderCursor(q)) - , m_preprocessorButton(0) , m_cppSelectionChanger() { } @@ -224,7 +229,15 @@ void CppEditorWidget::finalizeInitialization() connect(cmd, &Command::keySequenceChanged, this, &CppEditorWidget::updatePreprocessorButtonTooltip); updatePreprocessorButtonTooltip(); connect(d->m_preprocessorButton, &QAbstractButton::clicked, this, &CppEditorWidget::showPreProcessorWidget); + + d->m_headerErrorsIndicatorButton = new QToolButton(this); + d->m_headerErrorsIndicatorButton->setIcon(Utils::Icons::WARNING_TOOLBAR.pixmap()); + connect(d->m_headerErrorsIndicatorButton, &QAbstractButton::clicked, + this, &CppEditorWidget::showHeaderErrorInfoBar); + d->m_headerErrorsIndicatorButton->setEnabled(false); + insertExtraToolBarWidget(TextEditorWidget::Left, d->m_preprocessorButton); + insertExtraToolBarWidget(TextEditorWidget::Left, d->m_headerErrorsIndicatorButton); insertExtraToolBarWidget(TextEditorWidget::Left, d->m_cppEditorOutline->widget()); } @@ -287,6 +300,7 @@ void CppEditorWidget::onCppDocumentUpdated() void CppEditorWidget::onCodeWarningsUpdated(unsigned revision, const QList selections, + const HeaderErrorDiagnosticWidgetCreator &creator, const TextEditor::RefactorMarkers &refactorMarkers) { if (revision != documentRevision()) @@ -294,6 +308,9 @@ void CppEditorWidget::onCodeWarningsUpdated(unsigned revision, setExtraSelections(TextEditorWidget::CodeWarningsSelection, selections); setRefactorMarkers(refactorMarkersWithoutClangMarkers() + refactorMarkers); + + d->m_headerErrorDiagnosticWidgetCreator = creator; + updateHeaderErrorWidgets(); } void CppEditorWidget::onIfdefedOutBlocksUpdated(unsigned revision, @@ -304,6 +321,24 @@ void CppEditorWidget::onIfdefedOutBlocksUpdated(unsigned revision, setIfdefedOutBlocks(ifdefedOutBlocks); } +void CppEditorWidget::updateHeaderErrorWidgets() +{ + const Id id(Constants::ERRORS_IN_HEADER_FILES); + InfoBar *infoBar = textDocument()->infoBar(); + + infoBar->removeInfo(id); + + if (d->m_headerErrorDiagnosticWidgetCreator) { + if (infoBar->canInfoBeAdded(id)) { + addHeaderErrorInfoBarEntryAndHideIndicator(); + } else { + d->m_headerErrorsIndicatorButton->setEnabled(true); + } + } else { + d->m_headerErrorsIndicatorButton->setEnabled(false); + } +} + void CppEditorWidget::findUsages() { if (!d->m_modelManager) @@ -399,6 +434,25 @@ void CppEditorWidget::renameSymbolUnderCursorBuiltin() renameUsages(); // Rename non-local symbol or macro } +void CppEditorWidget::addHeaderErrorInfoBarEntryAndHideIndicator() const +{ + InfoBarEntry info(Constants::ERRORS_IN_HEADER_FILES, + tr("Warning: The code model could not parse an included file, " + "which might lead to slow or incorrect code completion and " + "highlighting, for example."), + InfoBarEntry::GlobalSuppressionEnabled); + info.setDetailsWidgetCreator(d->m_headerErrorDiagnosticWidgetCreator); + info.setShowDefaultCancelButton(false); + info.setSuppressionButtonInfo([this](){ + d->m_headerErrorsIndicatorButton->setEnabled(true); + }); + + InfoBar *infoBar = textDocument()->infoBar(); + infoBar->addInfo(info); + + d->m_headerErrorsIndicatorButton->setEnabled(false); +} + namespace { QList fetchProjectParts(CppTools::CppModelManager *modelManager, @@ -970,5 +1024,14 @@ void CppEditorWidget::showPreProcessorWidget() } } +void CppEditorWidget::showHeaderErrorInfoBar() +{ + const Id id(Constants::ERRORS_IN_HEADER_FILES); + QTC_CHECK(!textDocument()->infoBar()->canInfoBeAdded(id)); + + InfoBar::globallyUnsuppressInfo(id); + addHeaderErrorInfoBarEntryAndHideIndicator(); +} + } // namespace Internal } // namespace CppEditor diff --git a/src/plugins/cppeditor/cppeditor.h b/src/plugins/cppeditor/cppeditor.h index 7fb26e93c36..cce22cebcd4 100644 --- a/src/plugins/cppeditor/cppeditor.h +++ b/src/plugins/cppeditor/cppeditor.h @@ -31,6 +31,10 @@ #include +namespace Core { +class InfoBarEntry; +} + namespace CppTools { class CppEditorOutline; class RefactoringEngineInterface; @@ -87,6 +91,7 @@ public: void switchDeclarationDefinition(bool inNextSplit); void showPreProcessorWidget(); + void showHeaderErrorInfoBar(); void findUsages(); void renameSymbolUnderCursor(); @@ -108,6 +113,9 @@ protected: void slotCodeStyleSettingsChanged(const QVariant &) override; +public: + using HeaderErrorDiagnosticWidgetCreator = std::function; + private: void updateFunctionDeclDefLink(); void updateFunctionDeclDefLinkNow(); @@ -118,10 +126,12 @@ private: void onCodeWarningsUpdated(unsigned revision, const QList selections, + const HeaderErrorDiagnosticWidgetCreator &creator, const TextEditor::RefactorMarkers &refactorMarkers); void onIfdefedOutBlocksUpdated(unsigned revision, const QList ifdefedOutBlocks); + void updateHeaderErrorWidgets(); void updateSemanticInfo(const CppTools::SemanticInfo &semanticInfo, bool updateUseSelectionSynchronously = false); void updatePreprocessorButtonTooltip(); @@ -140,6 +150,8 @@ private: void renameSymbolUnderCursorClang(); void renameSymbolUnderCursorBuiltin(); + void addHeaderErrorInfoBarEntryAndHideIndicator() const; + CppTools::ProjectPart *projectPart() const; private: diff --git a/src/plugins/cppeditor/cppeditorconstants.h b/src/plugins/cppeditor/cppeditorconstants.h index 1d72be7a7a4..761822d1012 100644 --- a/src/plugins/cppeditor/cppeditorconstants.h +++ b/src/plugins/cppeditor/cppeditorconstants.h @@ -36,6 +36,7 @@ const char OPEN_DECLARATION_DEFINITION_IN_NEXT_SPLIT[] = "CppEditor.OpenDeclarat const char RENAME_SYMBOL_UNDER_CURSOR[] = "CppEditor.RenameSymbolUnderCursor"; const char FIND_USAGES[] = "CppEditor.FindUsages"; const char OPEN_PREPROCESSOR_DIALOG[] = "CppEditor.OpenPreprocessorDialog"; +const char ERRORS_IN_HEADER_FILES[] = "CppEditor.ErrorsInHeaderFiles"; const char M_REFACTORING_MENU_INSERTION_POINT[] = "CppEditor.RefactorGroup"; const char UPDATE_CODEMODEL[] = "CppEditor.UpdateCodeModel"; const char INSPECT_CPP_CODEMODEL[] = "CppEditor.InspectCppCodeModel"; diff --git a/src/plugins/cppeditor/cppeditordocument.h b/src/plugins/cppeditor/cppeditordocument.h index 953e24de3e1..985e9a41b6a 100644 --- a/src/plugins/cppeditor/cppeditordocument.h +++ b/src/plugins/cppeditor/cppeditordocument.h @@ -59,9 +59,13 @@ public: const QByteArray &defines); void scheduleProcessDocument(); +public: + using HeaderErrorDiagnosticWidgetCreator = std::function; + signals: void codeWarningsUpdated(unsigned contentsRevision, const QList selections, + const HeaderErrorDiagnosticWidgetCreator &creator, const TextEditor::RefactorMarkers &refactorMarkers); void ifdefedOutBlocksUpdated(unsigned contentsRevision, diff --git a/src/plugins/cpptools/baseeditordocumentprocessor.h b/src/plugins/cpptools/baseeditordocumentprocessor.h index da6a20ca943..3ac8a3524d9 100644 --- a/src/plugins/cpptools/baseeditordocumentprocessor.h +++ b/src/plugins/cpptools/baseeditordocumentprocessor.h @@ -37,6 +37,8 @@ #include +#include + namespace TextEditor { class TextDocument; class QuickFixOperations; @@ -69,10 +71,15 @@ public: virtual void editorDocumentTimerRestarted(); +public: + using HeaderErrorDiagnosticWidgetCreator = std::function; + signals: + // Signal interface to implement void codeWarningsUpdated(unsigned revision, const QList selections, + const HeaderErrorDiagnosticWidgetCreator &creator, const TextEditor::RefactorMarkers &refactorMarkers); void ifdefedOutBlocksUpdated(unsigned revision, diff --git a/src/plugins/cpptools/builtineditordocumentprocessor.cpp b/src/plugins/cpptools/builtineditordocumentprocessor.cpp index a7a7ac1a338..284e1af8ae9 100644 --- a/src/plugins/cpptools/builtineditordocumentprocessor.cpp +++ b/src/plugins/cpptools/builtineditordocumentprocessor.cpp @@ -310,7 +310,10 @@ void BuiltinEditorDocumentProcessor::onCodeWarningsUpdated( m_codeWarnings += toTextEditorSelections(codeWarnings, textDocument()); m_codeWarningsUpdated = true; - emit codeWarningsUpdated(revision(), m_codeWarnings, TextEditor::RefactorMarkers()); + emit codeWarningsUpdated(revision(), + m_codeWarnings, + HeaderErrorDiagnosticWidgetCreator(), + TextEditor::RefactorMarkers()); } SemanticInfo::Source BuiltinEditorDocumentProcessor::createSemanticInfoSource(bool force) const diff --git a/src/tools/clangbackend/ipcsource/clangrequestdocumentannotationsjob.cpp b/src/tools/clangbackend/ipcsource/clangrequestdocumentannotationsjob.cpp index 2fd806b192b..45b06c02037 100644 --- a/src/tools/clangbackend/ipcsource/clangrequestdocumentannotationsjob.cpp +++ b/src/tools/clangbackend/ipcsource/clangrequestdocumentannotationsjob.cpp @@ -40,7 +40,8 @@ static RequestDocumentAnnotationsJob::AsyncResult runAsyncHelper( RequestDocumentAnnotationsJob::AsyncResult asyncResult; - translationUnit.extractDocumentAnnotations(asyncResult.diagnostics, + translationUnit.extractDocumentAnnotations(asyncResult.firstHeaderErrorDiagnostic, + asyncResult.diagnostics, asyncResult.highlightingMarks, asyncResult.skippedSourceRanges); @@ -83,6 +84,7 @@ void RequestDocumentAnnotationsJob::sendAnnotations( { const DocumentAnnotationsChangedMessage message(m_pinnedFileContainer, result.diagnostics, + result.firstHeaderErrorDiagnostic, result.highlightingMarks, result.skippedSourceRanges); diff --git a/src/tools/clangbackend/ipcsource/clangrequestdocumentannotationsjob.h b/src/tools/clangbackend/ipcsource/clangrequestdocumentannotationsjob.h index b9ff1c822c7..e6bb4f9edca 100644 --- a/src/tools/clangbackend/ipcsource/clangrequestdocumentannotationsjob.h +++ b/src/tools/clangbackend/ipcsource/clangrequestdocumentannotationsjob.h @@ -36,6 +36,7 @@ namespace ClangBackEnd { struct RequestDocumentAnnotationsJobResult { + ClangBackEnd::DiagnosticContainer firstHeaderErrorDiagnostic; QVector diagnostics; QVector highlightingMarks; QVector skippedSourceRanges; diff --git a/src/tools/clangbackend/ipcsource/clangtranslationunit.cpp b/src/tools/clangbackend/ipcsource/clangtranslationunit.cpp index 2b963b21ca4..f4d3390038f 100644 --- a/src/tools/clangbackend/ipcsource/clangtranslationunit.cpp +++ b/src/tools/clangbackend/ipcsource/clangtranslationunit.cpp @@ -112,11 +112,12 @@ TranslationUnit::CodeCompletionResult TranslationUnit::complete( } void TranslationUnit::extractDocumentAnnotations( - QVector &diagnostics, + DiagnosticContainer &firstHeaderErrorDiagnostic, + QVector &mainFileDiagnostics, QVector &highlightingMarks, QVector &skippedSourceRanges) const { - diagnostics = mainFileDiagnostics(); + extractDiagnostics(firstHeaderErrorDiagnostic, mainFileDiagnostics); highlightingMarks = this->highlightingMarks().toHighlightingMarksContainers(); skippedSourceRanges = this->skippedSourceRanges().toSourceRangeContainers(); } @@ -126,15 +127,6 @@ DiagnosticSet TranslationUnit::diagnostics() const return DiagnosticSet(clang_getDiagnosticSetFromTU(m_cxTranslationUnit)); } -QVector TranslationUnit::mainFileDiagnostics() const -{ - const auto isMainFileDiagnostic = [this](const Diagnostic &diagnostic) { - return diagnostic.location().filePath() == m_filePath; - }; - - return diagnostics().toDiagnosticContainers(isMainFileDiagnostic); -} - SourceLocation TranslationUnit::sourceLocationAt(uint line,uint column) const { return SourceLocation(m_cxTranslationUnit, m_filePath, line, column); @@ -193,4 +185,35 @@ SkippedSourceRanges TranslationUnit::skippedSourceRanges() const return SkippedSourceRanges(m_cxTranslationUnit, m_filePath.constData()); } +static bool isMainFileDiagnostic(const Utf8String &mainFilePath, const Diagnostic &diagnostic) +{ + return diagnostic.location().filePath() == mainFilePath; +} + +static bool isHeaderErrorDiagnostic(const Utf8String &mainFilePath, const Diagnostic &diagnostic) +{ + const bool isCritical = diagnostic.severity() == DiagnosticSeverity::Error + || diagnostic.severity() == DiagnosticSeverity::Fatal; + return isCritical && diagnostic.location().filePath() != mainFilePath; +} + +void TranslationUnit::extractDiagnostics(DiagnosticContainer &firstHeaderErrorDiagnostic, + QVector &mainFileDiagnostics) const +{ + mainFileDiagnostics.clear(); + mainFileDiagnostics.reserve(int(diagnostics().size())); + + bool hasFirstHeaderErrorDiagnostic = false; + + for (const Diagnostic &diagnostic : diagnostics()) { + if (!hasFirstHeaderErrorDiagnostic && isHeaderErrorDiagnostic(m_filePath, diagnostic)) { + hasFirstHeaderErrorDiagnostic = true; + firstHeaderErrorDiagnostic = diagnostic.toDiagnosticContainer(); + } + + if (isMainFileDiagnostic(m_filePath, diagnostic)) + mainFileDiagnostics.push_back(diagnostic.toDiagnosticContainer()); + } +} + } // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangtranslationunit.h b/src/tools/clangbackend/ipcsource/clangtranslationunit.h index cf65dddc3a0..69574750923 100644 --- a/src/tools/clangbackend/ipcsource/clangtranslationunit.h +++ b/src/tools/clangbackend/ipcsource/clangtranslationunit.h @@ -76,12 +76,14 @@ public: CodeCompletionResult complete(UnsavedFiles &unsavedFiles, uint line, uint column) const; - void extractDocumentAnnotations(QVector &diagnostics, + void extractDiagnostics(DiagnosticContainer &firstHeaderErrorDiagnostic, + QVector &mainFileDiagnostics) const; + void extractDocumentAnnotations(DiagnosticContainer &firstHeaderErrorDiagnostic, + QVector &mainFileDiagnostics, QVector &highlightingMarks, QVector &skippedSourceRanges) const; DiagnosticSet diagnostics() const; - QVector mainFileDiagnostics() const; SourceLocation sourceLocationAt(uint line, uint column) const; SourceLocation sourceLocationAt(const Utf8String &filePath, uint line, uint column) const; diff --git a/src/tools/clangbackend/ipcsource/clangupdatedocumentannotationsjob.cpp b/src/tools/clangbackend/ipcsource/clangupdatedocumentannotationsjob.cpp index 4ece1fb2a0e..fee622964d8 100644 --- a/src/tools/clangbackend/ipcsource/clangupdatedocumentannotationsjob.cpp +++ b/src/tools/clangbackend/ipcsource/clangupdatedocumentannotationsjob.cpp @@ -45,7 +45,8 @@ static UpdateDocumentAnnotationsJob::AsyncResult runAsyncHelper( asyncResult.updateResult = translationUnit.update(translationUnitUpdatInput); // Collect - translationUnit.extractDocumentAnnotations(asyncResult.diagnostics, + translationUnit.extractDocumentAnnotations(asyncResult.firstHeaderErrorDiagnostic, + asyncResult.diagnostics, asyncResult.highlightingMarks, asyncResult.skippedSourceRanges); @@ -95,6 +96,7 @@ void UpdateDocumentAnnotationsJob::sendAnnotations(const AsyncResult &result) { const DocumentAnnotationsChangedMessage message(m_pinnedFileContainer, result.diagnostics, + result.firstHeaderErrorDiagnostic, result.highlightingMarks, result.skippedSourceRanges); diff --git a/src/tools/clangbackend/ipcsource/clangupdatedocumentannotationsjob.h b/src/tools/clangbackend/ipcsource/clangupdatedocumentannotationsjob.h index 5ddb9727dc0..00fe3be3110 100644 --- a/src/tools/clangbackend/ipcsource/clangupdatedocumentannotationsjob.h +++ b/src/tools/clangbackend/ipcsource/clangupdatedocumentannotationsjob.h @@ -39,6 +39,7 @@ struct UpdateDocumentAnnotationsJobResult { TranslationUnitUpdateResult updateResult; + ClangBackEnd::DiagnosticContainer firstHeaderErrorDiagnostic; QVector diagnostics; QVector highlightingMarks; QVector skippedSourceRanges; diff --git a/tests/unit/unittest/clangtranslationunit-test.cpp b/tests/unit/unittest/clangtranslationunit-test.cpp new file mode 100644 index 00000000000..10fee318ab9 --- /dev/null +++ b/tests/unit/unittest/clangtranslationunit-test.cpp @@ -0,0 +1,197 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 +#include +#include +#include + +#include + +#include +#include +#include +#include "gtest-qt-printing.h" + +using ClangBackEnd::DiagnosticContainer; +using ClangBackEnd::TranslationUnit; +using ClangBackEnd::TranslationUnitUpdateInput; +using ClangBackEnd::TranslationUnitUpdateResult; + +using testing::ContainerEq; +using testing::Eq; + +namespace { + +class TranslationUnit : public ::testing::Test +{ +protected: + void SetUp() override; + void TearDown() override; + + void parse(); + void reparse(); + + DiagnosticContainer createDiagnostic(const QString &text, + ClangBackEnd::DiagnosticSeverity severity, + uint line, + uint column, + const QString &filePath) const; + QVector diagnosticsFromMainFile() const; + QVector errorDiagnosticsFromHeaders() const; + +protected: + Utf8String sourceFilePath = Utf8StringLiteral(TESTDATA_DIR"/diagnostic_erroneous_source.cpp"); + Utf8String headerFilePath = Utf8StringLiteral(TESTDATA_DIR"/diagnostic_erroneous_header.h"); + CXIndex cxIndex = nullptr; + CXTranslationUnit cxTranslationUnit = nullptr; + ::TranslationUnit translationUnit{Utf8String(), sourceFilePath, cxIndex, cxTranslationUnit}; + + DiagnosticContainer extractedFirstHeaderErrorDiagnostic; + QVector extractedMainFileDiagnostics; +}; + +TEST_F(TranslationUnit, HasExpectedMainFileDiagnostics) +{ + translationUnit.extractDiagnostics(extractedFirstHeaderErrorDiagnostic, + extractedMainFileDiagnostics); + + ASSERT_THAT(extractedMainFileDiagnostics, ContainerEq(diagnosticsFromMainFile())); +} + +TEST_F(TranslationUnit, HasExpectedMainFileDiagnosticsAfterReparse) +{ + reparse(); + + translationUnit.extractDiagnostics(extractedFirstHeaderErrorDiagnostic, + extractedMainFileDiagnostics); + + ASSERT_THAT(extractedMainFileDiagnostics, ContainerEq(diagnosticsFromMainFile())); +} + +TEST_F(TranslationUnit, HasErrorDiagnosticsInHeaders) +{ + translationUnit.extractDiagnostics(extractedFirstHeaderErrorDiagnostic, + extractedMainFileDiagnostics); + + ASSERT_THAT(extractedFirstHeaderErrorDiagnostic, + Eq(errorDiagnosticsFromHeaders().first())); +} + +TEST_F(TranslationUnit, HasErrorDiagnosticsInHeadersAfterReparse) +{ + reparse(); + + translationUnit.extractDiagnostics(extractedFirstHeaderErrorDiagnostic, + extractedMainFileDiagnostics); + + ASSERT_THAT(extractedFirstHeaderErrorDiagnostic, + Eq(errorDiagnosticsFromHeaders().first())); +} + +void TranslationUnit::SetUp() +{ + parse(); +} + +void TranslationUnit::TearDown() +{ + clang_disposeTranslationUnit(cxTranslationUnit); + clang_disposeIndex(cxIndex); +} + +void TranslationUnit::parse() +{ + TranslationUnitUpdateInput parseInput; + parseInput.filePath = sourceFilePath; + parseInput.parseNeeded = true; + const TranslationUnitUpdateResult parseResult = translationUnit.update(parseInput); + ASSERT_TRUE(parseResult.hasParsed()); +} + +void TranslationUnit::reparse() +{ + TranslationUnitUpdateInput parseInput; + parseInput.filePath = sourceFilePath; + parseInput.reparseNeeded = true; + const TranslationUnitUpdateResult parseResult = translationUnit.update(parseInput); + ASSERT_TRUE(parseResult.hasReparsed()); +} + +DiagnosticContainer TranslationUnit::createDiagnostic(const QString &text, + ClangBackEnd::DiagnosticSeverity severity, + uint line, + uint column, + const QString &filePath) const +{ + return DiagnosticContainer( + text, + Utf8StringLiteral(""), + {}, + severity, + {filePath, line, column}, + {}, + {}, + {} + ); +} + +QVector TranslationUnit::diagnosticsFromMainFile() const +{ + return { + createDiagnostic( + QStringLiteral("warning: enumeration value 'Three' not handled in switch"), + ClangBackEnd::DiagnosticSeverity::Warning, + 7, + 13, + sourceFilePath), + createDiagnostic( + QStringLiteral("error: void function 'g' should not return a value"), + ClangBackEnd::DiagnosticSeverity::Error, + 15, + 5, + sourceFilePath), + createDiagnostic( + QStringLiteral("warning: using the result of an assignment as a condition without parentheses"), + ClangBackEnd::DiagnosticSeverity::Warning, + 21, + 11, + sourceFilePath), + }; +} + +QVector TranslationUnit::errorDiagnosticsFromHeaders() const +{ + return { + createDiagnostic( + QStringLiteral("error: C++ requires a type specifier for all declarations"), + ClangBackEnd::DiagnosticSeverity::Error, + 11, + 1, + headerFilePath), + }; +} + +} // anonymous namespace diff --git a/tests/unit/unittest/clientserverinprocess-test.cpp b/tests/unit/unittest/clientserverinprocess-test.cpp index 37d1e46570c..d4a1cab16dd 100644 --- a/tests/unit/unittest/clientserverinprocess-test.cpp +++ b/tests/unit/unittest/clientserverinprocess-test.cpp @@ -277,6 +277,7 @@ TEST_F(ClientServerInProcess, SendDocumentAnnotationsChangedMessage) ClangBackEnd::DocumentAnnotationsChangedMessage message(fileContainer, {diagnostic}, + {}, {highlightingMark}, QVector()); diff --git a/tests/unit/unittest/readandwritemessageblock-test.cpp b/tests/unit/unittest/readandwritemessageblock-test.cpp index d45a3a347ae..ace9effadb4 100644 --- a/tests/unit/unittest/readandwritemessageblock-test.cpp +++ b/tests/unit/unittest/readandwritemessageblock-test.cpp @@ -186,6 +186,7 @@ TEST_F(ReadAndWriteMessageBlock, CompareDocumentAnnotationsChangedMessage) CompareMessage(ClangBackEnd::DocumentAnnotationsChangedMessage(fileContainer, {diagnostic}, + {}, {highlightingMark}, QVector())); } diff --git a/tests/unit/unittest/unittest.pro b/tests/unit/unittest/unittest.pro index e847b8347fa..ce81a41a991 100644 --- a/tests/unit/unittest/unittest.pro +++ b/tests/unit/unittest/unittest.pro @@ -61,6 +61,7 @@ SOURCES += \ clangrequestdocumentannotationsjob-test.cpp \ clangsupportivetranslationunitinitializertest.cpp \ clangstring-test.cpp \ + clangtranslationunit-test.cpp \ clangtranslationunits-test.cpp \ clangupdatedocumentannotationsjob-test.cpp \ codecompletionsextractor-test.cpp \ From 63ae993bf2a844d8f6b25b9733827a6ccd651be3 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 11 Oct 2016 18:19:12 +0200 Subject: [PATCH 26/27] Clang: Add output argument highlighting This adds a mix-in for writable functions arguments. Change-Id: I758f7fef77d992ea25395db550571ccb081fd5fd Reviewed-by: Nikolai Kosjar --- .../clanghighlightingmarksreporter.cpp | 2 + .../texteditor/texteditorconstants.cpp | 1 + src/plugins/texteditor/texteditorconstants.h | 1 + src/plugins/texteditor/texteditorsettings.cpp | 4 + .../clangbackend/ipcsource/clangtype.cpp | 2 +- src/tools/clangbackend/ipcsource/clangtype.h | 2 +- src/tools/clangbackend/ipcsource/cursor.cpp | 61 ++++++++--- src/tools/clangbackend/ipcsource/cursor.h | 8 +- .../ipcsource/highlightingmark.cpp | 96 +++++++++++++++-- .../clangbackend/ipcsource/highlightingmark.h | 13 ++- .../ipcsource/highlightingmarks.cpp | 25 ++++- .../ipcsource/highlightingmarks.h | 3 + .../ipcsource/highlightingmarksiterator.h | 19 +++- tests/unit/unittest/clangcompareoperators.h | 40 +++++++ tests/unit/unittest/cursor-test.cpp | 40 +++---- .../unit/unittest/data/highlightingmarks.cpp | 86 ++++++++++++++- .../unit/unittest/highlightingmarks-test.cpp | 100 ++++++++++++++++++ tests/unit/unittest/unittest.pro | 1 + 18 files changed, 447 insertions(+), 57 deletions(-) create mode 100644 tests/unit/unittest/clangcompareoperators.h diff --git a/src/plugins/clangcodemodel/clanghighlightingmarksreporter.cpp b/src/plugins/clangcodemodel/clanghighlightingmarksreporter.cpp index 2e85c49c3cf..07d57419188 100644 --- a/src/plugins/clangcodemodel/clanghighlightingmarksreporter.cpp +++ b/src/plugins/clangcodemodel/clanghighlightingmarksreporter.cpp @@ -60,6 +60,8 @@ TextEditor::TextStyle toTextStyle(ClangBackEnd::HighlightingType type) return TextEditor::C_PREPROCESSOR; case HighlightingType::Declaration: return TextEditor::C_DECLARATION; + case HighlightingType::OutputArgument: + return TextEditor::C_OUTPUT_ARGUMENT; default: return TextEditor::C_TEXT; // never called } diff --git a/src/plugins/texteditor/texteditorconstants.cpp b/src/plugins/texteditor/texteditorconstants.cpp index d670895512a..48681e5cd06 100644 --- a/src/plugins/texteditor/texteditorconstants.cpp +++ b/src/plugins/texteditor/texteditorconstants.cpp @@ -102,6 +102,7 @@ const char *nameForStyle(TextStyle style) case C_WARNING_CONTEXT: return "WarningContext"; case C_DECLARATION: return "Declaration"; + case C_OUTPUT_ARGUMENT: return "C_OUTPUT_ARGUMENT"; case C_LAST_STYLE_SENTINEL: return "LastStyleSentinel"; } diff --git a/src/plugins/texteditor/texteditorconstants.h b/src/plugins/texteditor/texteditorconstants.h index f68b41cc0db..8dbaf93de4f 100644 --- a/src/plugins/texteditor/texteditorconstants.h +++ b/src/plugins/texteditor/texteditorconstants.h @@ -100,6 +100,7 @@ enum TextStyle : quint8 { C_ERROR_CONTEXT, C_DECLARATION, + C_OUTPUT_ARGUMENT, C_LAST_STYLE_SENTINEL }; diff --git a/src/plugins/texteditor/texteditorsettings.cpp b/src/plugins/texteditor/texteditorsettings.cpp index 67270d81bbd..0660f929f80 100644 --- a/src/plugins/texteditor/texteditorsettings.cpp +++ b/src/plugins/texteditor/texteditorsettings.cpp @@ -313,6 +313,10 @@ TextEditorSettings::TextEditorSettings(QObject *parent) tr("Declaration"), tr("Declaration of a function, variable, and so on."), FormatDescription::ShowFontUnderlineAndRelativeControls); + formatDescr.emplace_back(C_OUTPUT_ARGUMENT, + tr("Output Argument"), + tr("Writable arguments of a function call."), + FormatDescription::ShowFontUnderlineAndRelativeControls); d->m_fontSettingsPage = new FontSettingsPage(formatDescr, Constants::TEXT_EDITOR_FONT_SETTINGS, diff --git a/src/tools/clangbackend/ipcsource/clangtype.cpp b/src/tools/clangbackend/ipcsource/clangtype.cpp index 54fe1aba2d6..13ab8c9c6c0 100644 --- a/src/tools/clangbackend/ipcsource/clangtype.cpp +++ b/src/tools/clangbackend/ipcsource/clangtype.cpp @@ -69,7 +69,7 @@ bool Type::isReferencingConstant() const return (isPointer() || isLValueReference()) && pointeeType().isConstant(); } -bool Type::isOutputParameter() const +bool Type::isOutputArgument() const { return (isPointer() || isLValueReference()) && !pointeeType().isConstant(); } diff --git a/src/tools/clangbackend/ipcsource/clangtype.h b/src/tools/clangbackend/ipcsource/clangtype.h index e9a19e92db9..6a51245ce6b 100644 --- a/src/tools/clangbackend/ipcsource/clangtype.h +++ b/src/tools/clangbackend/ipcsource/clangtype.h @@ -49,7 +49,7 @@ public: bool isConstantPointer() const; bool isLValueReference() const; bool isReferencingConstant() const; - bool isOutputParameter() const; + bool isOutputArgument() const; Utf8String utf8Spelling() const; ClangString spelling() const; diff --git a/src/tools/clangbackend/ipcsource/cursor.cpp b/src/tools/clangbackend/ipcsource/cursor.cpp index f88ef02bcda..56dfd9e610d 100644 --- a/src/tools/clangbackend/ipcsource/cursor.cpp +++ b/src/tools/clangbackend/ipcsource/cursor.cpp @@ -213,16 +213,32 @@ SourceLocation Cursor::sourceLocation() const return clang_getCursorLocation(cxCursor); } +CXSourceLocation Cursor::cxSourceLocation() const +{ + return clang_getCursorLocation(cxCursor); +} + SourceRange Cursor::sourceRange() const { return clang_getCursorExtent(cxCursor); } +CXSourceRange Cursor::cxSourceRange() const +{ + return clang_getCursorExtent(cxCursor); +} + SourceRange Cursor::commentRange() const { return clang_Cursor_getCommentRange(cxCursor); } +bool Cursor::hasSameSourceLocationAs(const Cursor &other) const +{ + return clang_equalLocations(clang_getCursorLocation(cxCursor), + clang_getCursorLocation(other.cxCursor)); +} + Cursor Cursor::definition() const { return clang_getCursorDefinition(cxCursor); @@ -279,32 +295,42 @@ Cursor Cursor::argument(int index) const { return clang_Cursor_getArgument(cxCursor, index); } + namespace { -void collectOutputArguments(const Cursor &callExpression, - std::vector &outputArguments) + +bool isNotUnexposedLValueReference(const Cursor &argument, const Type &argumentType) { - auto callExpressionType = callExpression.referenced().type(); - auto argumentCount = callExpression.argumentCount(); - outputArguments.reserve(argumentCount); + return !(argument.isUnexposed() && argumentType.isLValueReference()); +} + +} + +void Cursor::collectOutputArgumentRangesTo(std::vector &outputArgumentRanges) const +{ + const Type callExpressionType = referenced().type(); + const int argumentCount = this->argumentCount(); + const std::size_t maxSize = std::size_t(std::max(0, argumentCount)) + + outputArgumentRanges.size(); + outputArgumentRanges.reserve(maxSize); for (int argumentIndex = 0; argumentIndex < argumentCount; ++argumentIndex) { - auto argument = callExpression.argument(argumentIndex); - auto argumentType = callExpressionType.argument(argumentIndex); + const Cursor argument = this->argument(argumentIndex); + const Type argumentType = callExpressionType.argument(argumentIndex); - if (!argument.isUnexposed() && argumentType.isOutputParameter()) - outputArguments.push_back(callExpression.argument(argumentIndex)); + if (isNotUnexposedLValueReference(argument, argumentType) + && argumentType.isOutputArgument()) { + outputArgumentRanges.push_back(argument.cxSourceRange()); + } } } -} -std::vector Cursor::outputArguments() const +std::vector Cursor::outputArgumentRanges() const { - std::vector outputArguments; + std::vector outputArgumentRanges; - if (kind() == CXCursor_CallExpr) - collectOutputArguments(*this, outputArguments); + collectOutputArgumentRangesTo(outputArgumentRanges); - return outputArguments; + return outputArgumentRanges; } CXCursorKind Cursor::kind() const @@ -317,6 +343,11 @@ bool operator==(const Cursor &first, const Cursor &second) return clang_equalCursors(first.cxCursor, second.cxCursor); } +bool operator!=(const Cursor &first, const Cursor &second) +{ + return !(first == second); +} + void PrintTo(CXCursorKind cursorKind, ::std::ostream *os) { ClangString cursorKindSpelling(clang_getCursorKindSpelling(cursorKind)); diff --git a/src/tools/clangbackend/ipcsource/cursor.h b/src/tools/clangbackend/ipcsource/cursor.h index 6711ab98d51..8f294c7e26c 100644 --- a/src/tools/clangbackend/ipcsource/cursor.h +++ b/src/tools/clangbackend/ipcsource/cursor.h @@ -78,8 +78,11 @@ public: Type nonPointerTupe() const; SourceLocation sourceLocation() const; + CXSourceLocation cxSourceLocation() const; SourceRange sourceRange() const; + CXSourceRange cxSourceRange() const; SourceRange commentRange() const; + bool hasSameSourceLocationAs(const Cursor &other) const; Cursor definition() const; Cursor canonical() const; @@ -90,7 +93,9 @@ public: Cursor functionBaseDeclaration() const; Cursor functionBase() const; Cursor argument(int index) const; - std::vector outputArguments() const; + void collectOutputArgumentRangesTo( + std::vector &outputArgumentRanges) const; + std::vector outputArgumentRanges() const; CXCursorKind kind() const; @@ -114,6 +119,7 @@ void Cursor::visit(VisitorCallback visitorCallback) const } bool operator==(const Cursor &first, const Cursor &second); +bool operator!=(const Cursor &first, const Cursor &second); void PrintTo(CXCursorKind cursorKind, ::std::ostream *os); void PrintTo(const Cursor &cursor, ::std::ostream* os); diff --git a/src/tools/clangbackend/ipcsource/highlightingmark.cpp b/src/tools/clangbackend/ipcsource/highlightingmark.cpp index 18ceca91579..d4744f78534 100644 --- a/src/tools/clangbackend/ipcsource/highlightingmark.cpp +++ b/src/tools/clangbackend/ipcsource/highlightingmark.cpp @@ -30,6 +30,7 @@ #include "highlightingmark.h" #include "sourcelocation.h" #include "sourcerange.h" +#include "sourcerangecontainer.h" #include #include @@ -39,16 +40,19 @@ namespace ClangBackEnd { HighlightingMark::HighlightingMark(const CXCursor &cxCursor, - CXToken *cxToken, - CXTranslationUnit cxTranslationUnit) + CXToken *cxToken, + CXTranslationUnit cxTranslationUnit, + std::vector ¤tOutputArgumentRanges) + : currentOutputArgumentRanges(¤tOutputArgumentRanges), + originalCursor(cxCursor) { const SourceRange sourceRange = clang_getTokenExtent(cxTranslationUnit, *cxToken); const auto start = sourceRange.start(); const auto end = sourceRange.end(); - originalCursor = cxCursor; line = start.line(); column = start.column(); + offset = start.offset(); length = end.offset() - start.offset(); collectKinds(cxToken, originalCursor); } @@ -159,6 +163,17 @@ void HighlightingMark::variableKind(const Cursor &cursor) types.mainHighlightingType = HighlightingType::LocalVariable; else types.mainHighlightingType = HighlightingType::GlobalVariable; + + if (isOutputArgument()) + types.mixinHighlightingTypes.push_back(HighlightingType::OutputArgument); +} + +void HighlightingMark::fieldKind(const Cursor &) +{ + types.mainHighlightingType = HighlightingType::Field; + + if (isOutputArgument()) + types.mixinHighlightingTypes.push_back(HighlightingType::OutputArgument); } bool HighlightingMark::isVirtualMethodDeclarationOrDefinition(const Cursor &cursor) const @@ -185,6 +200,68 @@ void HighlightingMark::addExtraTypeIfFirstPass(HighlightingType type, types.mixinHighlightingTypes.push_back(type); } +bool HighlightingMark::isArgumentInCurrentOutputArgumentLocations() const +{ + auto originalSourceLocation = originalCursor.cxSourceLocation(); + + const auto isNotSameOutputArgument = [&] (const CXSourceRange ¤tSourceRange) { + return !(originalSourceLocation.int_data >= currentSourceRange.begin_int_data + && originalSourceLocation.int_data <= currentSourceRange.end_int_data); + }; + + auto partitionPoint = std::partition(currentOutputArgumentRanges->begin(), + currentOutputArgumentRanges->end(), + isNotSameOutputArgument); + + bool isOutputArgument = partitionPoint != currentOutputArgumentRanges->end(); + + if (isOutputArgument) + currentOutputArgumentRanges->erase(partitionPoint, currentOutputArgumentRanges->end()); + + return isOutputArgument; +} + +bool HighlightingMark::isOutputArgument() const +{ + if (currentOutputArgumentRanges->empty()) + return false; + + return isArgumentInCurrentOutputArgumentLocations(); +} + +void HighlightingMark::collectOutputArguments(const Cursor &cursor) +{ + cursor.collectOutputArgumentRangesTo(*currentOutputArgumentRanges); + filterOutPreviousOutputArguments(); +} + +namespace { + +uint getStart(CXSourceRange cxSourceRange) +{ + CXSourceLocation startSourceLocation = clang_getRangeStart(cxSourceRange); + + uint startOffset; + + clang_getFileLocation(startSourceLocation, nullptr, nullptr, nullptr, &startOffset); + + return startOffset; +} +} + +void HighlightingMark::filterOutPreviousOutputArguments() +{ + auto isAfterLocation = [this] (CXSourceRange outputRange) { + return getStart(outputRange) > offset; + }; + + auto precedingBegin = std::partition(currentOutputArgumentRanges->begin(), + currentOutputArgumentRanges->end(), + isAfterLocation); + + currentOutputArgumentRanges->erase(precedingBegin, currentOutputArgumentRanges->end()); +} + void HighlightingMark::functionKind(const Cursor &cursor, Recursion recursion) { if (isRealDynamicCall(cursor) || isVirtualMethodDeclarationOrDefinition(cursor)) @@ -204,12 +281,13 @@ void HighlightingMark::identifierKind(const Cursor &cursor, Recursion recursion) case CXCursor_CallExpr: case CXCursor_CXXMethod: functionKind(cursor, recursion); break; case CXCursor_NonTypeTemplateParameter: - case CXCursor_ParmDecl: types.mainHighlightingType = HighlightingType::LocalVariable; break; + case CXCursor_CompoundStmt: types.mainHighlightingType = HighlightingType::LocalVariable; break; + case CXCursor_ParmDecl: case CXCursor_VarDecl: variableKind(cursor); break; case CXCursor_DeclRefExpr: identifierKind(cursor.referenced(), Recursion::RecursivePass); break; case CXCursor_MemberRefExpr: memberReferenceKind(cursor); break; case CXCursor_FieldDecl: - case CXCursor_MemberRef: + case CXCursor_MemberRef: fieldKind(cursor); break; case CXCursor_ObjCIvarDecl: case CXCursor_ObjCPropertyDecl: case CXCursor_ObjCClassMethodDecl: @@ -282,15 +360,17 @@ HighlightingType operatorKind(const Cursor &cursor) return HighlightingType::Invalid; } -HighlightingType punctationKind(const Cursor &cursor) +} + +HighlightingType HighlightingMark::punctuationKind(const Cursor &cursor) { switch (cursor.kind()) { case CXCursor_DeclRefExpr: return operatorKind(cursor); + case CXCursor_CallExpr: collectOutputArguments(cursor); default: return HighlightingType::Invalid; } } -} void HighlightingMark::collectKinds(CXToken *cxToken, const Cursor &cursor) { auto cxTokenKind = clang_getTokenKind(*cxToken); @@ -299,7 +379,7 @@ void HighlightingMark::collectKinds(CXToken *cxToken, const Cursor &cursor) switch (cxTokenKind) { case CXToken_Keyword: types.mainHighlightingType = HighlightingType::Keyword; break; - case CXToken_Punctuation: types.mainHighlightingType = punctationKind(cursor); break; + case CXToken_Punctuation: types.mainHighlightingType = punctuationKind(cursor); break; case CXToken_Identifier: identifierKind(cursor, Recursion::FirstPass); break; case CXToken_Comment: types.mainHighlightingType = HighlightingType::Comment; break; case CXToken_Literal: types.mainHighlightingType = literalKind(cursor); break; diff --git a/src/tools/clangbackend/ipcsource/highlightingmark.h b/src/tools/clangbackend/ipcsource/highlightingmark.h index 7f2009810b5..17408527305 100644 --- a/src/tools/clangbackend/ipcsource/highlightingmark.h +++ b/src/tools/clangbackend/ipcsource/highlightingmark.h @@ -45,7 +45,10 @@ class HighlightingMark }; public: - HighlightingMark(const CXCursor &cxCursor, CXToken *cxToken, CXTranslationUnit cxTranslationUnit); + HighlightingMark(const CXCursor &cxCursor, + CXToken *cxToken, + CXTranslationUnit cxTranslationUnit, + std::vector ¤tOutputArgumentRanges); HighlightingMark(uint line, uint column, uint length, HighlightingTypes types); HighlightingMark(uint line, uint column, uint length, HighlightingType type); @@ -61,18 +64,26 @@ private: void identifierKind(const Cursor &cursor, Recursion recursion); void referencedTypeKind(const Cursor &cursor); void variableKind(const Cursor &cursor); + void fieldKind(const Cursor &cursor); bool isVirtualMethodDeclarationOrDefinition(const Cursor &cursor) const; void functionKind(const Cursor &cursor, Recursion recursion); void memberReferenceKind(const Cursor &cursor); + HighlightingType punctuationKind(const Cursor &cursor); void collectKinds(CXToken *cxToken, const Cursor &cursor); bool isRealDynamicCall(const Cursor &cursor) const; void addExtraTypeIfFirstPass(HighlightingType type, Recursion recursion); + bool isOutputArgument() const; + void collectOutputArguments(const Cursor &cursor); + void filterOutPreviousOutputArguments(); + bool isArgumentInCurrentOutputArgumentLocations() const; private: + std::vector *currentOutputArgumentRanges = nullptr; Cursor originalCursor; uint line; uint column; uint length; + uint offset = 0; HighlightingTypes types; }; diff --git a/src/tools/clangbackend/ipcsource/highlightingmarks.cpp b/src/tools/clangbackend/ipcsource/highlightingmarks.cpp index 10cb2b5e82b..725e6eb3169 100644 --- a/src/tools/clangbackend/ipcsource/highlightingmarks.cpp +++ b/src/tools/clangbackend/ipcsource/highlightingmarks.cpp @@ -47,12 +47,18 @@ HighlightingMarks::~HighlightingMarks() HighlightingMarks::const_iterator HighlightingMarks::begin() const { - return const_iterator(cxCursor.cbegin(), cxToken, cxTranslationUnit); + return const_iterator(cxCursor.cbegin(), + cxToken, + cxTranslationUnit, + currentOutputArgumentRanges); } HighlightingMarks::const_iterator HighlightingMarks::end() const { - return const_iterator(cxCursor.cend(), cxToken + cxTokenCount, cxTranslationUnit); + return const_iterator(cxCursor.cend(), + cxToken + cxTokenCount, + cxTranslationUnit, + currentOutputArgumentRanges); } QVector HighlightingMarks::toHighlightingMarksContainers() const @@ -67,11 +73,19 @@ QVector HighlightingMarks::toHighlightingMarksContain && !highlightMark.hasMainType(HighlightingType::Comment); }; - std::copy_if(begin(), end(), std::back_inserter(containers), isValidHighlightMark); + for (const HighlightingMark &highlightMark : *this) { + if (isValidHighlightMark(highlightMark)) + containers.push_back(highlightMark); + } return containers; } +bool HighlightingMarks::currentOutputArgumentRangesAreEmpty() const +{ + return currentOutputArgumentRanges.empty(); +} + bool HighlightingMarks::isEmpty() const { return cxTokenCount == 0; @@ -89,7 +103,10 @@ uint HighlightingMarks::size() const HighlightingMark HighlightingMarks::operator[](size_t index) const { - return HighlightingMark(cxCursor[index], cxToken + index, cxTranslationUnit); + return HighlightingMark(cxCursor[index], + cxToken + index, + cxTranslationUnit, + currentOutputArgumentRanges); } } // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/highlightingmarks.h b/src/tools/clangbackend/ipcsource/highlightingmarks.h index 2d77bb86c53..6904bb31847 100644 --- a/src/tools/clangbackend/ipcsource/highlightingmarks.h +++ b/src/tools/clangbackend/ipcsource/highlightingmarks.h @@ -58,7 +58,10 @@ public: QVector toHighlightingMarksContainers() const; + bool currentOutputArgumentRangesAreEmpty() const; + private: + mutable std::vector currentOutputArgumentRanges; CXTranslationUnit cxTranslationUnit = nullptr; CXToken *const cxToken = nullptr; const uint cxTokenCount = 0; diff --git a/src/tools/clangbackend/ipcsource/highlightingmarksiterator.h b/src/tools/clangbackend/ipcsource/highlightingmarksiterator.h index 9cbfcabaad6..daeada47a76 100644 --- a/src/tools/clangbackend/ipcsource/highlightingmarksiterator.h +++ b/src/tools/clangbackend/ipcsource/highlightingmarksiterator.h @@ -43,11 +43,13 @@ class HighlightingMarksIterator : public std::iterator::const_iterator cxCursorIterator, - CXToken *cxToken, - CXTranslationUnit cxTranslationUnit) + CXToken *cxToken, + CXTranslationUnit cxTranslationUnit, + std::vector ¤tOutputArgumentRanges) : cxCursorIterator(cxCursorIterator), cxToken(cxToken), - cxTranslationUnit(cxTranslationUnit) + cxTranslationUnit(cxTranslationUnit), + currentOutputArgumentRanges(currentOutputArgumentRanges) {} HighlightingMarksIterator& operator++() @@ -60,7 +62,10 @@ public: HighlightingMarksIterator operator++(int) { - return HighlightingMarksIterator(cxCursorIterator++, cxToken++, cxTranslationUnit); + return HighlightingMarksIterator(cxCursorIterator++, + cxToken++, + cxTranslationUnit, + currentOutputArgumentRanges); } bool operator==(HighlightingMarksIterator other) const @@ -75,13 +80,17 @@ public: HighlightingMark operator*() { - return HighlightingMark(*cxCursorIterator, cxToken, cxTranslationUnit); + return HighlightingMark(*cxCursorIterator, + cxToken, + cxTranslationUnit, + currentOutputArgumentRanges); } private: std::vector::const_iterator cxCursorIterator; CXToken *cxToken; CXTranslationUnit cxTranslationUnit; + std::vector ¤tOutputArgumentRanges; }; } // namespace ClangBackEnd diff --git a/tests/unit/unittest/clangcompareoperators.h b/tests/unit/unittest/clangcompareoperators.h new file mode 100644 index 00000000000..39351946beb --- /dev/null +++ b/tests/unit/unittest/clangcompareoperators.h @@ -0,0 +1,40 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 + +inline +bool operator==(const CXSourceLocation &first, const CXSourceLocation &second) +{ + return clang_equalLocations(first, second); +} + +inline +bool operator==(const CXSourceRange &first, const CXSourceRange &second) +{ + return clang_equalRanges(first, second); +} diff --git a/tests/unit/unittest/cursor-test.cpp b/tests/unit/unittest/cursor-test.cpp index bba963be869..77df29a17a5 100644 --- a/tests/unit/unittest/cursor-test.cpp +++ b/tests/unit/unittest/cursor-test.cpp @@ -25,6 +25,8 @@ #include "googletest.h" +#include "clangcompareoperators.h" + #include #include #include @@ -85,6 +87,8 @@ protected: const TranslationUnit &translationUnit = d->translationUnit; }; + + TEST_F(Cursor, CreateNullCursor) { ::Cursor cursor; @@ -489,21 +493,21 @@ TEST_F(Cursor, HasNotFinaAttributeInClass) TEST_F(Cursor, HasOutputValues) { auto callExpressionCursor = translationUnit.cursorAt(117, 19); - auto outputArgumentExpectedCursor = translationUnit.cursorAt(117, 20); + auto outputArgumentExpectedSourceLocation = translationUnit.cursorAt(117, 20).cxSourceRange(); - auto outputArguments = callExpressionCursor.outputArguments(); + auto outputArgumentLocations = callExpressionCursor.outputArgumentRanges(); - ASSERT_THAT(outputArguments.size(), 1); - ASSERT_THAT(outputArguments[0], outputArgumentExpectedCursor); + ASSERT_THAT(outputArgumentLocations.size(), 2); + ASSERT_THAT(outputArgumentLocations[0], outputArgumentExpectedSourceLocation); } TEST_F(Cursor, HasOnlyInputValues) { auto callExpressionCursor = translationUnit.cursorAt(118, 18); - auto outputArguments = callExpressionCursor.outputArguments(); + auto outputArgumentLocations = callExpressionCursor.outputArgumentRanges(); - ASSERT_THAT(outputArguments, IsEmpty()); + ASSERT_THAT(outputArgumentLocations, IsEmpty()); } TEST_F(Cursor, ArgumentCountIsZero) @@ -748,58 +752,58 @@ TEST_F(Cursor, PointerIsNotRefencingConstant) ASSERT_FALSE(argument.isReferencingConstant()); } -TEST_F(Cursor, PointerIsOutputParameter) +TEST_F(Cursor, PointerIsOutputArgument) { auto callExpressionCursor = translationUnit.cursorAt(127, 13); auto argument = callExpressionCursor.type().argument(0); - ASSERT_TRUE(argument.isOutputParameter()); + ASSERT_TRUE(argument.isOutputArgument()); } -TEST_F(Cursor, ConstantReferenceIsNotOutputParameter) +TEST_F(Cursor, ConstantReferenceIsNotOutputArgument) { auto callExpressionCursor = translationUnit.cursorAt(125, 26); auto argument = callExpressionCursor.type().argument(0); - ASSERT_FALSE(argument.isOutputParameter()); + ASSERT_FALSE(argument.isOutputArgument()); } -TEST_F(Cursor, PointerToConstantIsNotOutputParameter) +TEST_F(Cursor, PointerToConstantIsNotOutputArgument) { auto callExpressionCursor = translationUnit.cursorAt(126, 20); auto argument = callExpressionCursor.type().argument(0); - ASSERT_FALSE(argument.isOutputParameter()) << argument.isConstant() << argument.pointeeType().isConstant(); + ASSERT_FALSE(argument.isOutputArgument()) << argument.isConstant() << argument.pointeeType().isConstant(); } -TEST_F(Cursor, ConstantPointerIsNotOutputParameter) +TEST_F(Cursor, ConstantPointerIsNotOutputArgument) { auto callExpressionCursor = translationUnit.cursorAt(128, 21); auto argument = callExpressionCursor.type().argument(0); - ASSERT_TRUE(argument.isOutputParameter()); + ASSERT_TRUE(argument.isOutputArgument()); } -TEST_F(Cursor, ReferenceIsOutputParameter) +TEST_F(Cursor, ReferenceIsOutputArgument) { auto callExpressionCursor = translationUnit.cursorAt(124, 21); auto argument = callExpressionCursor.type().argument(0); - ASSERT_TRUE(argument.isOutputParameter()); + ASSERT_TRUE(argument.isOutputArgument()); } -TEST_F(Cursor, ConstReferenceIsNotOutputParameter) +TEST_F(Cursor, ConstReferenceIsNotOutputArgument) { auto callExpressionCursor = translationUnit.cursorAt(125, 26); auto argument = callExpressionCursor.type().argument(0); - ASSERT_FALSE(argument.isOutputParameter()); + ASSERT_FALSE(argument.isOutputArgument()); } Data *Cursor::d; diff --git a/tests/unit/unittest/data/highlightingmarks.cpp b/tests/unit/unittest/data/highlightingmarks.cpp index 0fb2ea3bebc..d37f3e297af 100644 --- a/tests/unit/unittest/data/highlightingmarks.cpp +++ b/tests/unit/unittest/data/highlightingmarks.cpp @@ -278,12 +278,12 @@ void FinalClass::FinalClassThisCall() } -void OutputParameter(int &one, const int &two, int *three=0); +void OutputArgument(int &one, const int &two, int *three=0); -void f12() +void f12b() { int One; - OutputParameter(One, 2); + OutputArgument(One, 2); } #include @@ -445,3 +445,83 @@ struct LambdaTester lambda(var2); } }; + +void NonConstReferenceArgument(int &argument); + +void f22() +{ + int x = 1; + + NonConstReferenceArgument(x); +} + +void ConstReferenceArgument(const int &argument); + +void f23() +{ + int x = 1; + + ConstReferenceArgument(x); +} + +void RValueReferenceArgument(int &&argument); + +void f24() +{ + int x = 1; + + RValueReferenceArgument(static_cast(x)); +} + +void NonConstPointerArgument(int *argument); + +void f25() +{ + int *x; + + NonConstPointerArgument(x); +} + +void ConstPointerArgument(const int *argument); + +void f26() +{ + int *x; + + ConstPointerArgument(x); +} + +void NonConstReferenceArgumentCallInsideCall(int x, int &argument); +int GetArgument(int x); + +void f27() +{ + int x = 1; + + NonConstReferenceArgumentCallInsideCall(GetArgument(x), x); +} + +void f28(int &Reference) +{ + NonConstReferenceArgument(Reference); +} + +void f29() +{ + int x; + + NonConstPointerArgument(&x); +} + +struct NonConstPointerArgumentAsMemberOfClass +{ + int member; +}; + +void f30() +{ + NonConstPointerArgumentAsMemberOfClass instance; + + NonConstReferenceArgument(instance.member); +} + diff --git a/tests/unit/unittest/highlightingmarks-test.cpp b/tests/unit/unittest/highlightingmarks-test.cpp index 1d3ba1a05ec..9433bf9249e 100644 --- a/tests/unit/unittest/highlightingmarks-test.cpp +++ b/tests/unit/unittest/highlightingmarks-test.cpp @@ -959,6 +959,106 @@ TEST_F(HighlightingMarks, TypeDefDeclarationUsage) ASSERT_THAT(infos[0], HasOnlyType(HighlightingType::Type)); } +TEST_F(HighlightingMarks, NonConstReferenceArgument) +{ + const auto infos = translationUnit.highlightingMarksInRange(sourceRange(455, 35)); + + infos[1]; + + ASSERT_THAT(infos[2], + HasTwoTypes(HighlightingType::LocalVariable, HighlightingType::OutputArgument)); +} + +TEST_F(HighlightingMarks, ConstReferenceArgument) +{ + const auto infos = translationUnit.highlightingMarksInRange(sourceRange(464, 32)); + + infos[1]; + + ASSERT_THAT(infos[2], + HasOnlyType(HighlightingType::LocalVariable)); +} + +TEST_F(HighlightingMarks, RValueReferenceArgument) +{ + const auto infos = translationUnit.highlightingMarksInRange(sourceRange(473, 52)); + + infos[1]; + + ASSERT_THAT(infos[8], + HasOnlyType(HighlightingType::LocalVariable)); +} + +TEST_F(HighlightingMarks, NonConstPointerArgument) +{ + const auto infos = translationUnit.highlightingMarksInRange(sourceRange(482, 33)); + + infos[1]; + + ASSERT_THAT(infos[2], + HasTwoTypes(HighlightingType::LocalVariable, HighlightingType::OutputArgument)); +} + +TEST_F(HighlightingMarks, ConstPointerArgument) +{ + const auto infos = translationUnit.highlightingMarksInRange(sourceRange(491, 30)); + + infos[1]; + + ASSERT_THAT(infos[2], + HasOnlyType(HighlightingType::LocalVariable)); +} + +TEST_F(HighlightingMarks, NonConstReferenceArgumentCallInsideCall) +{ + const auto infos = translationUnit.highlightingMarksInRange(sourceRange(501, 64)); + infos[1]; + + infos[3]; + + ASSERT_THAT(infos[7], + HasTwoTypes(HighlightingType::LocalVariable, HighlightingType::OutputArgument)); +} + +TEST_F(HighlightingMarks, OutputArgumentsAreEmptyAfterIteration) +{ + const auto infos = translationUnit.highlightingMarksInRange(sourceRange(501, 63)); + + for (const auto &info : infos ) {} + + ASSERT_TRUE(infos.currentOutputArgumentRangesAreEmpty()); +} + +TEST_F(HighlightingMarks, NonConstReferenceArgumentFromFunctionParameter) +{ + const auto infos = translationUnit.highlightingMarksInRange(sourceRange(506, 42)); + + infos[1]; + + ASSERT_THAT(infos[2], + HasTwoTypes(HighlightingType::LocalVariable, HighlightingType::OutputArgument)); +} + +TEST_F(HighlightingMarks, NonConstPointerArgumentAsExpression) +{ + const auto infos = translationUnit.highlightingMarksInRange(sourceRange(513, 33)); + + infos[1]; + + ASSERT_THAT(infos[3], + HasTwoTypes(HighlightingType::LocalVariable, HighlightingType::OutputArgument)); +} + +TEST_F(HighlightingMarks, NonConstPointerArgumentAsMemberOfClass) +{ + const auto infos = translationUnit.highlightingMarksInRange(sourceRange(525, 46)); + + infos[1]; + + ASSERT_THAT(infos[4], + HasTwoTypes(HighlightingType::Field, HighlightingType::OutputArgument)); +} + TEST_F(HighlightingMarks, DISABLED_EnumerationTypeDef) { const auto infos = translationUnit.highlightingMarksInRange(sourceRange(424, 41)); diff --git a/tests/unit/unittest/unittest.pro b/tests/unit/unittest/unittest.pro index ce81a41a991..aa4b560fb6b 100644 --- a/tests/unit/unittest/unittest.pro +++ b/tests/unit/unittest/unittest.pro @@ -118,6 +118,7 @@ HEADERS += \ chunksreportedmonitor.h \ clangasyncjob-base.h \ diagnosticcontainer-matcher.h \ + clangcompareoperators.h \ dummyclangipcclient.h \ mockclangcodemodelclient.h \ mockclangcodemodelserver.h From 9c2d96c965568216e8ec1c2ba5ef5c6554302ffc Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Wed, 12 Oct 2016 09:53:49 +0200 Subject: [PATCH 27/27] Help/macOS: Remove use of deprecated function Change-Id: I8bffe04ab8900879417063041f4ee48886e31480 Reviewed-by: Christian Stenger --- src/plugins/help/macwebkithelpviewer.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/help/macwebkithelpviewer.mm b/src/plugins/help/macwebkithelpviewer.mm index ca9095b6417..97982f49574 100644 --- a/src/plugins/help/macwebkithelpviewer.mm +++ b/src/plugins/help/macwebkithelpviewer.mm @@ -169,7 +169,7 @@ static QPoint flipPoint(const NSPoint &p) NSURL *resolvedURL = data.resolvedUrl.toNSURL(); NSString *mimeType = data.mimeType.toNSString(); - NSData *nsdata = QtMac::toNSData(data.data); // Qt 5.3 has this in QByteArray + NSData *nsdata = data.data.toNSData(); NSURLResponse *response = [[NSURLResponse alloc] initWithURL:resolvedURL MIMEType:mimeType expectedContentLength:data.data.length()