// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "cppsemanticinfoupdater.h" #include "cppmodelmanager.h" #include #include #include #include #include #include #include enum { debug = 0 }; using namespace CPlusPlus; using namespace QtTaskTree; using namespace Utils; static Q_LOGGING_CATEGORY(log, "qtc.cppeditor.semanticinfoupdater", QtWarningMsg) namespace CppEditor { class SemanticInfoUpdaterPrivate { public: SemanticInfo m_semanticInfo; QSingleTaskTreeRunner m_taskTreeRunner; }; class FuturizedTopLevelDeclarationProcessor: public TopLevelDeclarationProcessor { public: explicit FuturizedTopLevelDeclarationProcessor(const QFuture &future): m_future(future) {} bool processDeclaration(DeclarationAST *) override { return !m_future.isCanceled(); } private: QFuture m_future; }; static void doUpdate(QPromise &promise, const SemanticInfo::Source &source) { SemanticInfo newSemanticInfo; newSemanticInfo.revision = source.revision; newSemanticInfo.snapshot = source.snapshot; Document::Ptr doc = newSemanticInfo.snapshot.preprocessedDocument(source.code, source.filePath); FuturizedTopLevelDeclarationProcessor processor(QFuture(promise.future())); doc->control()->setTopLevelDeclarationProcessor(&processor); doc->check(); if (promise.isCanceled()) newSemanticInfo.complete = false; newSemanticInfo.doc = doc; qCDebug(log) << "update() for source revision:" << source.revision << "canceled:" << !newSemanticInfo.complete; promise.addResult(newSemanticInfo); } static std::optional canReuseSemanticInfo( const SemanticInfo ¤tSemanticInfo, const SemanticInfo::Source &source) { if (!source.force && currentSemanticInfo.complete && currentSemanticInfo.revision == source.revision && currentSemanticInfo.doc && currentSemanticInfo.doc->translationUnit()->ast() && currentSemanticInfo.doc->filePath() == source.filePath && !currentSemanticInfo.snapshot.isEmpty() && currentSemanticInfo.snapshot == source.snapshot) { SemanticInfo newSemanticInfo; newSemanticInfo.revision = source.revision; newSemanticInfo.snapshot = source.snapshot; newSemanticInfo.doc = currentSemanticInfo.doc; qCDebug(log) << "re-using current semantic info, source revision:" << source.revision; return newSemanticInfo; } return {}; } SemanticInfoUpdater::SemanticInfoUpdater() : d(new SemanticInfoUpdaterPrivate) {} SemanticInfoUpdater::~SemanticInfoUpdater() = default; SemanticInfo SemanticInfoUpdater::update(const SemanticInfo::Source &source) { qCDebug(log) << "update() - synchronous"; d->m_taskTreeRunner.reset(); const auto info = canReuseSemanticInfo(d->m_semanticInfo, source); if (info) { d->m_semanticInfo = *info; return d->m_semanticInfo; } QPromise dummy; dummy.start(); doUpdate(dummy, source); const SemanticInfo result = dummy.future().result(); d->m_semanticInfo = result; return result; } void SemanticInfoUpdater::updateDetached(const SemanticInfo::Source &source) { qCDebug(log) << "updateDetached() - asynchronous"; d->m_taskTreeRunner.reset(); const auto info = canReuseSemanticInfo(d->m_semanticInfo, source); if (info) { d->m_semanticInfo = *info; emit updated(d->m_semanticInfo); return; } const auto onSetup = [source](Async &task) { task.setConcurrentCallData(&doUpdate, source); task.setThreadPool(CppModelManager::sharedThreadPool()); }; const auto onDone = [this](const Async &task) { d->m_semanticInfo = task.result(); emit updated(d->m_semanticInfo); }; d->m_taskTreeRunner.start({AsyncTask(onSetup, onDone)}); } SemanticInfo SemanticInfoUpdater::semanticInfo() const { return d->m_semanticInfo; } } // namespace CppEditor