aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/cppeditor/cppsemanticinfoupdater.cpp
blob: bbc3fb5fd345636f2fe31098ae9809b33b90f3d8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// 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 <cplusplus/Control.h>
#include <cplusplus/CppDocument.h>
#include <cplusplus/TranslationUnit.h>

#include <QtTaskTree/QSingleTaskTreeRunner>

#include <utils/async.h>
#include <utils/qtcassert.h>

#include <QLoggingCategory>

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<void> &future): m_future(future) {}
    bool processDeclaration(DeclarationAST *) override { return !m_future.isCanceled(); }
private:
    QFuture<void> m_future;
};

static void doUpdate(QPromise<SemanticInfo> &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<void>(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<SemanticInfo> canReuseSemanticInfo(
    const SemanticInfo &currentSemanticInfo, 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<SemanticInfo> 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<SemanticInfo> &task) {
        task.setConcurrentCallData(&doUpdate, source);
        task.setThreadPool(CppModelManager::sharedThreadPool());
    };
    const auto onDone = [this](const Async<SemanticInfo> &task) {
        d->m_semanticInfo = task.result();
        emit updated(d->m_semanticInfo);
    };
    d->m_taskTreeRunner.start({AsyncTask<SemanticInfo>(onSetup, onDone)});
}

SemanticInfo SemanticInfoUpdater::semanticInfo() const
{
    return d->m_semanticInfo;
}

} // namespace CppEditor