forked from qt-creator/qt-creator
ClangCodeModel: Try harder to find out whether a function is virtual
... when following symbols with clangd. The textdocument/implementation request is expensive, so we'd like to make sure we only run it if we are sure that we're really dealing with a virtual function. We re-use the information gathered during highlighting for this purpose. Change-Id: Id92a9a92fe2ac7fd5acf903a9ade711223ee401b Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -27,6 +27,7 @@
|
|||||||
|
|
||||||
#include "clangcompletioncontextanalyzer.h"
|
#include "clangcompletioncontextanalyzer.h"
|
||||||
#include "clangdiagnosticmanager.h"
|
#include "clangdiagnosticmanager.h"
|
||||||
|
#include "clangmodelmanagersupport.h"
|
||||||
#include "clangpreprocessorassistproposalitem.h"
|
#include "clangpreprocessorassistproposalitem.h"
|
||||||
#include "clangtextmark.h"
|
#include "clangtextmark.h"
|
||||||
#include "clangutils.h"
|
#include "clangutils.h"
|
||||||
@@ -654,6 +655,7 @@ public:
|
|||||||
bool openInSplit)
|
bool openInSplit)
|
||||||
: q(q), id(id), cursor(cursor), editorWidget(editorWidget), uri(uri),
|
: q(q), id(id), cursor(cursor), editorWidget(editorWidget), uri(uri),
|
||||||
callback(std::move(callback)), virtualFuncAssistProvider(q->d),
|
callback(std::move(callback)), virtualFuncAssistProvider(q->d),
|
||||||
|
docRevision(editorWidget ? editorWidget->textDocument()->document()->revision() : -1),
|
||||||
openInSplit(openInSplit) {}
|
openInSplit(openInSplit) {}
|
||||||
|
|
||||||
~FollowSymbolData()
|
~FollowSymbolData()
|
||||||
@@ -678,6 +680,8 @@ public:
|
|||||||
openedFiles.clear();
|
openedFiles.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool defLinkIsAmbiguous() const;
|
||||||
|
|
||||||
ClangdClient * const q;
|
ClangdClient * const q;
|
||||||
const quint64 id;
|
const quint64 id;
|
||||||
const QTextCursor cursor;
|
const QTextCursor cursor;
|
||||||
@@ -688,6 +692,7 @@ public:
|
|||||||
QList<MessageId> pendingSymbolInfoRequests;
|
QList<MessageId> pendingSymbolInfoRequests;
|
||||||
QList<MessageId> pendingGotoImplRequests;
|
QList<MessageId> pendingGotoImplRequests;
|
||||||
QList<MessageId> pendingGotoDefRequests;
|
QList<MessageId> pendingGotoDefRequests;
|
||||||
|
const int docRevision;
|
||||||
const bool openInSplit;
|
const bool openInSplit;
|
||||||
|
|
||||||
Utils::Link defLink;
|
Utils::Link defLink;
|
||||||
@@ -1059,6 +1064,10 @@ public:
|
|||||||
// The highlighters are owned by their respective documents.
|
// The highlighters are owned by their respective documents.
|
||||||
std::unordered_map<TextDocument *, CppEditor::SemanticHighlighter *> highlighters;
|
std::unordered_map<TextDocument *, CppEditor::SemanticHighlighter *> highlighters;
|
||||||
|
|
||||||
|
// The ranges of symbols referring to virtual functions, with document version,
|
||||||
|
// as extracted by the highlighting procedure.
|
||||||
|
QHash<TextDocument *, QPair<QList<Range>, int>> virtualRanges;
|
||||||
|
|
||||||
VersionedDataCache<const TextDocument *, AstNode> astCache;
|
VersionedDataCache<const TextDocument *, AstNode> astCache;
|
||||||
VersionedDataCache<Utils::FilePath, AstNode> externalAstCache;
|
VersionedDataCache<Utils::FilePath, AstNode> externalAstCache;
|
||||||
TaskTimer highlightingTimer{"highlighting"};
|
TaskTimer highlightingTimer{"highlighting"};
|
||||||
@@ -1366,6 +1375,7 @@ void ClangdClient::handleDocumentClosed(TextDocument *doc)
|
|||||||
{
|
{
|
||||||
d->highlighters.erase(doc);
|
d->highlighters.erase(doc);
|
||||||
d->astCache.remove(doc);
|
d->astCache.remove(doc);
|
||||||
|
d->virtualRanges.remove(doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
QVersionNumber ClangdClient::versionNumber() const
|
QVersionNumber ClangdClient::versionNumber() const
|
||||||
@@ -1996,6 +2006,14 @@ void ClangdClient::gatherHelpItemForTooltip(const HoverRequest::Response &hoverR
|
|||||||
d->getAndHandleAst(doc, astHandler, Private::AstCallbackMode::SyncIfPossible);
|
d->getAndHandleAst(doc, astHandler, Private::AstCallbackMode::SyncIfPossible);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClangdClient::setVirtualRanges(const Utils::FilePath &filePath, const QList<Range> &ranges,
|
||||||
|
int revision)
|
||||||
|
{
|
||||||
|
TextDocument * const doc = documentForFilePath(filePath);
|
||||||
|
if (doc && doc->document()->revision() == revision)
|
||||||
|
d->virtualRanges.insert(doc, {ranges, revision});
|
||||||
|
}
|
||||||
|
|
||||||
void ClangdClient::Private::handleGotoDefinitionResult()
|
void ClangdClient::Private::handleGotoDefinitionResult()
|
||||||
{
|
{
|
||||||
QTC_ASSERT(followSymbolData->defLink.hasValidTarget(), return);
|
QTC_ASSERT(followSymbolData->defLink.hasValidTarget(), return);
|
||||||
@@ -2003,8 +2021,7 @@ void ClangdClient::Private::handleGotoDefinitionResult()
|
|||||||
qCDebug(clangdLog) << "handling go to definition result";
|
qCDebug(clangdLog) << "handling go to definition result";
|
||||||
|
|
||||||
// No dis-ambiguation necessary. Call back with the link and finish.
|
// No dis-ambiguation necessary. Call back with the link and finish.
|
||||||
if (!followSymbolData->cursorNode->mightBeAmbiguousVirtualCall()
|
if (!followSymbolData->defLinkIsAmbiguous()) {
|
||||||
&& !followSymbolData->cursorNode->isPureVirtualDeclaration()) {
|
|
||||||
followSymbolData->callback(followSymbolData->defLink);
|
followSymbolData->callback(followSymbolData->defLink);
|
||||||
followSymbolData.reset();
|
followSymbolData.reset();
|
||||||
return;
|
return;
|
||||||
@@ -2491,6 +2508,20 @@ static void semanticHighlighter(QFutureInterface<HighlightingResult> &future,
|
|||||||
ExtraHighlightingResultsCollector(future, results, filePath, ast, &doc, docContents).collect();
|
ExtraHighlightingResultsCollector(future, results, filePath, ast, &doc, docContents).collect();
|
||||||
if (!future.isCanceled()) {
|
if (!future.isCanceled()) {
|
||||||
qCDebug(clangdLog) << "reporting" << results.size() << "highlighting results";
|
qCDebug(clangdLog) << "reporting" << results.size() << "highlighting results";
|
||||||
|
QList<Range> virtualRanges;
|
||||||
|
for (const HighlightingResult &r : results) {
|
||||||
|
if (r.textStyles.mainStyle != C_VIRTUAL_METHOD)
|
||||||
|
continue;
|
||||||
|
const Position startPos(r.line - 1, r.column - 1);
|
||||||
|
virtualRanges << Range(startPos, startPos.withOffset(r.length, &doc));
|
||||||
|
}
|
||||||
|
QMetaObject::invokeMethod(ClangModelManagerSupport::instance(),
|
||||||
|
[filePath, virtualRanges, docRevision] {
|
||||||
|
if (ClangdClient * const client
|
||||||
|
= ClangModelManagerSupport::instance()->clientForFile(filePath)) {
|
||||||
|
client->setVirtualRanges(filePath, virtualRanges, docRevision);
|
||||||
|
}
|
||||||
|
}, Qt::QueuedConnection);
|
||||||
future.reportResults(QVector<HighlightingResult>(results.cbegin(),
|
future.reportResults(QVector<HighlightingResult>(results.cbegin(),
|
||||||
results.cend()));
|
results.cend()));
|
||||||
}
|
}
|
||||||
@@ -3463,6 +3494,24 @@ void ExtraHighlightingResultsCollector::visitNode(const AstNode &node)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ClangdClient::FollowSymbolData::defLinkIsAmbiguous() const
|
||||||
|
{
|
||||||
|
// If we have up-to-date highlighting info, we can give a definite answer.
|
||||||
|
if (editorWidget) {
|
||||||
|
const auto virtualRanges = q->d->virtualRanges.constFind(editorWidget->textDocument());
|
||||||
|
if (virtualRanges != q->d->virtualRanges.constEnd()
|
||||||
|
&& virtualRanges->second == docRevision) {
|
||||||
|
const auto matcher = [cursorRange = cursorNode->range()](const Range &r) {
|
||||||
|
return cursorRange.overlaps(r);
|
||||||
|
};
|
||||||
|
return Utils::contains(virtualRanges->first, matcher);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, we have to rely on AST-based heuristics.
|
||||||
|
return cursorNode->mightBeAmbiguousVirtualCall() || cursorNode->isPureVirtualDeclaration();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace ClangCodeModel
|
} // namespace ClangCodeModel
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
|
|
||||||
namespace Core { class SearchResultItem; }
|
namespace Core { class SearchResultItem; }
|
||||||
namespace CppEditor { class CppEditorWidget; }
|
namespace CppEditor { class CppEditorWidget; }
|
||||||
|
namespace LanguageServerProtocol { class Range; }
|
||||||
namespace ProjectExplorer { class Project; }
|
namespace ProjectExplorer { class Project; }
|
||||||
namespace TextEditor { class BaseTextEditor; }
|
namespace TextEditor { class BaseTextEditor; }
|
||||||
|
|
||||||
@@ -76,6 +77,9 @@ public:
|
|||||||
const LanguageServerProtocol::HoverRequest::Response &hoverResponse,
|
const LanguageServerProtocol::HoverRequest::Response &hoverResponse,
|
||||||
const LanguageServerProtocol::DocumentUri &uri);
|
const LanguageServerProtocol::DocumentUri &uri);
|
||||||
|
|
||||||
|
void setVirtualRanges(const Utils::FilePath &filePath,
|
||||||
|
const QList<LanguageServerProtocol::Range> &ranges, int revision);
|
||||||
|
|
||||||
void enableTesting();
|
void enableTesting();
|
||||||
bool testingEnabled() const;
|
bool testingEnabled() const;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user