aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/cppeditor/cppsemanticinfoupdater.cpp
blob: e398c14b8612af05f6effec349f2915755b39a01 (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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// 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 <extensionsystem/pluginmanager.h>

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

#include <QLoggingCategory>

enum { debug = 0 };

using namespace CPlusPlus;
using namespace Utils;

static Q_LOGGING_CATEGORY(log, "qtc.cppeditor.semanticinfoupdater", QtWarningMsg)

namespace CppEditor {

class SemanticInfoUpdaterPrivate
{
public:
    ~SemanticInfoUpdaterPrivate() { cancelFuture(); }

    void cancelFuture();

    SemanticInfo m_semanticInfo;
    std::unique_ptr<QFutureWatcher<SemanticInfo>> m_watcher;
};

void SemanticInfoUpdaterPrivate::cancelFuture()
{
    if (!m_watcher)
        return;

    m_watcher->cancel();
    m_watcher.reset();
}

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, FilePath::fromString(source.fileName));

    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().toString() == source.fileName
            && !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->cancelFuture();

    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->cancelFuture();

    const auto info = canReuseSemanticInfo(d->m_semanticInfo, source);
    if (info) {
        d->m_semanticInfo = *info;
        emit updated(d->m_semanticInfo);
        return;
    }

    d->m_watcher.reset(new QFutureWatcher<SemanticInfo>);
    connect(d->m_watcher.get(), &QFutureWatcherBase::finished, this, [this] {
        d->m_semanticInfo = d->m_watcher->result();
        emit updated(d->m_semanticInfo);
        d->m_watcher.release()->deleteLater();
    });
    const auto future = Utils::asyncRun(CppModelManager::sharedThreadPool(), doUpdate, source);
    d->m_watcher->setFuture(future);
    ExtensionSystem::PluginManager::futureSynchronizer()->addFuture(future);
}

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

} // namespace CppEditor