forked from qt-creator/qt-creator
ClangCodeModel: Fix links for virtual overrides
For some reason, clangd returns the declaration instead of the definition position in the "Goto Implementation" result, so we have to do another look-up for each override. Change-Id: I2a99eb0dacdea07d5882087445dc2b2d61b24e58 Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -51,4 +51,11 @@ Link Link::fromString(const QString &fileName, bool canContainLineNumber, QStrin
|
|||||||
lineColumn.column};
|
lineColumn.column};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint qHash(const Link &l)
|
||||||
|
{
|
||||||
|
QString s = l.targetFilePath.toString();
|
||||||
|
return qHash(s.append(':').append(QString::number(l.targetLine)).append(':')
|
||||||
|
.append(QString::number(l.targetColumn)));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Utils
|
} // namespace Utils
|
||||||
|
@@ -62,6 +62,7 @@ public:
|
|||||||
&& linkTextStart == other.linkTextStart
|
&& linkTextStart == other.linkTextStart
|
||||||
&& linkTextEnd == other.linkTextEnd;
|
&& linkTextEnd == other.linkTextEnd;
|
||||||
}
|
}
|
||||||
|
bool operator!=(const Link &other) const { return !(*this == other); }
|
||||||
|
|
||||||
int linkTextStart = -1;
|
int linkTextStart = -1;
|
||||||
int linkTextEnd = -1;
|
int linkTextEnd = -1;
|
||||||
@@ -71,6 +72,8 @@ public:
|
|||||||
int targetColumn;
|
int targetColumn;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
uint QTCREATOR_UTILS_EXPORT qHash(const Link &l);
|
||||||
|
|
||||||
using ProcessLinkCallback = std::function<void(const Link &)>;
|
using ProcessLinkCallback = std::function<void(const Link &)>;
|
||||||
|
|
||||||
} // namespace Utils
|
} // namespace Utils
|
||||||
|
@@ -465,6 +465,8 @@ public:
|
|||||||
q->cancelRequest(id);
|
q->cancelRequest(id);
|
||||||
for (const MessageId &id : qAsConst(pendingGotoImplRequests))
|
for (const MessageId &id : qAsConst(pendingGotoImplRequests))
|
||||||
q->cancelRequest(id);
|
q->cancelRequest(id);
|
||||||
|
for (const MessageId &id : qAsConst(pendingGotoDefRequests))
|
||||||
|
q->cancelRequest(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void closeTempDocuments()
|
void closeTempDocuments()
|
||||||
@@ -492,10 +494,12 @@ public:
|
|||||||
VirtualFunctionAssistProvider virtualFuncAssistProvider;
|
VirtualFunctionAssistProvider virtualFuncAssistProvider;
|
||||||
QList<MessageId> pendingSymbolInfoRequests;
|
QList<MessageId> pendingSymbolInfoRequests;
|
||||||
QList<MessageId> pendingGotoImplRequests;
|
QList<MessageId> pendingGotoImplRequests;
|
||||||
|
QList<MessageId> pendingGotoDefRequests;
|
||||||
const bool openInSplit;
|
const bool openInSplit;
|
||||||
|
|
||||||
Utils::Link defLink;
|
Utils::Link defLink;
|
||||||
QList<Utils::Link> allLinks;
|
QList<Utils::Link> allLinks;
|
||||||
|
QHash<Utils::Link, Utils::Link> declDefMap;
|
||||||
AstNode cursorNode;
|
AstNode cursorNode;
|
||||||
AstNode defLinkNode;
|
AstNode defLinkNode;
|
||||||
SymbolDataList symbolsToDisplay;
|
SymbolDataList symbolsToDisplay;
|
||||||
@@ -581,6 +585,7 @@ ClangdClient::~ClangdClient()
|
|||||||
d->followSymbolData->openedFiles.clear();
|
d->followSymbolData->openedFiles.clear();
|
||||||
d->followSymbolData->pendingSymbolInfoRequests.clear();
|
d->followSymbolData->pendingSymbolInfoRequests.clear();
|
||||||
d->followSymbolData->pendingGotoImplRequests.clear();
|
d->followSymbolData->pendingGotoImplRequests.clear();
|
||||||
|
d->followSymbolData->pendingGotoDefRequests.clear();
|
||||||
}
|
}
|
||||||
delete d;
|
delete d;
|
||||||
}
|
}
|
||||||
@@ -1015,6 +1020,8 @@ void ClangdClient::Private::handleGotoImplementationResult(
|
|||||||
// Make a symbol info request for each link to get the class names.
|
// Make a symbol info request for each link to get the class names.
|
||||||
// Also get the AST for the base declaration, so we can find out whether it's
|
// Also get the AST for the base declaration, so we can find out whether it's
|
||||||
// pure virtual and mark it accordingly.
|
// pure virtual and mark it accordingly.
|
||||||
|
// In addition, we need to follow all override links, because for these, clangd
|
||||||
|
// gives us the declaration instead of the definition.
|
||||||
for (const Utils::Link &link : qAsConst(followSymbolData->allLinks)) {
|
for (const Utils::Link &link : qAsConst(followSymbolData->allLinks)) {
|
||||||
if (!q->documentForFilePath(link.targetFilePath)
|
if (!q->documentForFilePath(link.targetFilePath)
|
||||||
&& followSymbolData->openedFiles.insert(link.targetFilePath).second) {
|
&& followSymbolData->openedFiles.insert(link.targetFilePath).second) {
|
||||||
@@ -1022,8 +1029,9 @@ void ClangdClient::Private::handleGotoImplementationResult(
|
|||||||
}
|
}
|
||||||
const TextDocumentIdentifier doc(DocumentUri::fromFilePath(link.targetFilePath));
|
const TextDocumentIdentifier doc(DocumentUri::fromFilePath(link.targetFilePath));
|
||||||
const Position pos(link.targetLine - 1, link.targetColumn);
|
const Position pos(link.targetLine - 1, link.targetColumn);
|
||||||
SymbolInfoRequest req(TextDocumentPositionParams(doc, pos));
|
const TextDocumentPositionParams params(doc, pos);
|
||||||
req.setResponseCallback([this, link, id = followSymbolData->id, reqId = req.id()](
|
SymbolInfoRequest symReq(params);
|
||||||
|
symReq.setResponseCallback([this, link, id = followSymbolData->id, reqId = symReq.id()](
|
||||||
const SymbolInfoRequest::Response &response) {
|
const SymbolInfoRequest::Response &response) {
|
||||||
qCDebug(clangdLog) << "handling symbol info reply"
|
qCDebug(clangdLog) << "handling symbol info reply"
|
||||||
<< link.targetFilePath.toUserOutput() << link.targetLine;
|
<< link.targetFilePath.toUserOutput() << link.targetLine;
|
||||||
@@ -1043,13 +1051,48 @@ void ClangdClient::Private::handleGotoImplementationResult(
|
|||||||
}
|
}
|
||||||
followSymbolData->pendingSymbolInfoRequests.removeOne(reqId);
|
followSymbolData->pendingSymbolInfoRequests.removeOne(reqId);
|
||||||
if (followSymbolData->pendingSymbolInfoRequests.isEmpty()
|
if (followSymbolData->pendingSymbolInfoRequests.isEmpty()
|
||||||
|
&& followSymbolData->pendingGotoDefRequests.isEmpty()
|
||||||
&& followSymbolData->defLinkNode.isValid()) {
|
&& followSymbolData->defLinkNode.isValid()) {
|
||||||
handleDocumentInfoResults();
|
handleDocumentInfoResults();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
followSymbolData->pendingSymbolInfoRequests << req.id();
|
followSymbolData->pendingSymbolInfoRequests << symReq.id();
|
||||||
qCDebug(clangdLog) << "sending symbol info request";
|
qCDebug(clangdLog) << "sending symbol info request";
|
||||||
q->sendContent(req);
|
q->sendContent(symReq);
|
||||||
|
|
||||||
|
if (link == followSymbolData->defLink)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
GotoDefinitionRequest defReq(params);
|
||||||
|
defReq.setResponseCallback([this, link, id = followSymbolData->id, reqId = defReq.id()]
|
||||||
|
(const GotoDefinitionRequest::Response &response) {
|
||||||
|
qCDebug(clangdLog) << "handling additional go to definition reply for"
|
||||||
|
<< link.targetFilePath << link.targetLine;
|
||||||
|
if (!followSymbolData || id != followSymbolData->id)
|
||||||
|
return;
|
||||||
|
Utils::Link newLink;
|
||||||
|
if (Utils::optional<GotoResult> _result = response.result()) {
|
||||||
|
const GotoResult result = _result.value();
|
||||||
|
if (const auto ploc = Utils::get_if<Location>(&result)) {
|
||||||
|
newLink = ploc->toLink();
|
||||||
|
} else if (const auto plloc = Utils::get_if<QList<Location>>(&result)) {
|
||||||
|
if (!plloc->isEmpty())
|
||||||
|
newLink = plloc->value(0).toLink();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
qCDebug(clangdLog) << "def link is" << newLink.targetFilePath << newLink.targetLine;
|
||||||
|
followSymbolData->declDefMap.insert(link, newLink);
|
||||||
|
followSymbolData->pendingGotoDefRequests.removeOne(reqId);
|
||||||
|
if (followSymbolData->pendingSymbolInfoRequests.isEmpty()
|
||||||
|
&& followSymbolData->pendingGotoDefRequests.isEmpty()
|
||||||
|
&& followSymbolData->defLinkNode.isValid()) {
|
||||||
|
handleDocumentInfoResults();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
followSymbolData->pendingGotoDefRequests << defReq.id();
|
||||||
|
qCDebug(clangdLog) << "sending additional go to definition request"
|
||||||
|
<< link.targetFilePath << link.targetLine;
|
||||||
|
q->sendContent(defReq);
|
||||||
}
|
}
|
||||||
|
|
||||||
const DocumentUri defLinkUri
|
const DocumentUri defLinkUri
|
||||||
@@ -1066,8 +1109,10 @@ void ClangdClient::Private::handleGotoImplementationResult(
|
|||||||
const auto result = response.result();
|
const auto result = response.result();
|
||||||
if (result)
|
if (result)
|
||||||
followSymbolData->defLinkNode = *result;
|
followSymbolData->defLinkNode = *result;
|
||||||
if (followSymbolData->pendingSymbolInfoRequests.isEmpty())
|
if (followSymbolData->pendingSymbolInfoRequests.isEmpty()
|
||||||
|
&& followSymbolData->pendingGotoDefRequests.isEmpty()) {
|
||||||
handleDocumentInfoResults();
|
handleDocumentInfoResults();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
qCDebug(clangdLog) << "sending ast request for def link";
|
qCDebug(clangdLog) << "sending ast request for def link";
|
||||||
q->sendContent(astRequest);
|
q->sendContent(astRequest);
|
||||||
@@ -1107,10 +1152,17 @@ void ClangdClient::VirtualFunctionAssistProcessor::finalize()
|
|||||||
{
|
{
|
||||||
QList<TextEditor::AssistProposalItemInterface *> items;
|
QList<TextEditor::AssistProposalItemInterface *> items;
|
||||||
for (const SymbolData &symbol : qAsConst(m_data->followSymbolData->symbolsToDisplay)) {
|
for (const SymbolData &symbol : qAsConst(m_data->followSymbolData->symbolsToDisplay)) {
|
||||||
|
Utils::Link link = symbol.second;
|
||||||
|
const bool isOriginalLink = m_data->followSymbolData->defLink == symbol.second;
|
||||||
|
if (!isOriginalLink) {
|
||||||
|
const Utils::Link defLink = m_data->followSymbolData->declDefMap.value(symbol.second);
|
||||||
|
if (defLink.hasValidTarget())
|
||||||
|
link = defLink;
|
||||||
|
}
|
||||||
const auto item = new CppTools::VirtualFunctionProposalItem(
|
const auto item = new CppTools::VirtualFunctionProposalItem(
|
||||||
symbol.second, m_data->followSymbolData->openInSplit);
|
link, m_data->followSymbolData->openInSplit);
|
||||||
QString text = symbol.first;
|
QString text = symbol.first;
|
||||||
if (m_data->followSymbolData->defLink == symbol.second
|
if (isOriginalLink
|
||||||
&& (m_data->followSymbolData->defLinkNode.isPureVirtualDeclaration()
|
&& (m_data->followSymbolData->defLinkNode.isPureVirtualDeclaration()
|
||||||
|| m_data->followSymbolData->defLinkNode.isPureVirtualDefinition())) {
|
|| m_data->followSymbolData->defLinkNode.isPureVirtualDefinition())) {
|
||||||
text += " = 0";
|
text += " = 0";
|
||||||
|
@@ -516,14 +516,8 @@ F2TestCase::F2TestCase(CppEditorAction action,
|
|||||||
if (useClangd)
|
if (useClangd)
|
||||||
QEXPECT_FAIL("allOverrides from base declaration", "FIXME: check why this fails", Abort);
|
QEXPECT_FAIL("allOverrides from base declaration", "FIXME: check why this fails", Abort);
|
||||||
QCOMPARE(finalVirtualSymbolResults.size(), expectedVirtualFunctionProposal.size());
|
QCOMPARE(finalVirtualSymbolResults.size(), expectedVirtualFunctionProposal.size());
|
||||||
if (useClangd) {
|
if (useClangd)
|
||||||
QEXPECT_FAIL("allOverrides", "FIXME: clangd sometimes goes to decl instead of def", Abort);
|
|
||||||
QEXPECT_FAIL("possibleOverrides1", "FIXME: clangd sometimes goes to decl instead of def",
|
|
||||||
Abort);
|
|
||||||
QEXPECT_FAIL("possibleOverrides2", "FIXME: clangd sometimes goes to decl instead of def",
|
|
||||||
Abort);
|
|
||||||
QEXPECT_FAIL("itemOrder", "FIXME: sort items", Abort);
|
QEXPECT_FAIL("itemOrder", "FIXME: sort items", Abort);
|
||||||
}
|
|
||||||
QCOMPARE(finalVirtualSymbolResults, expectedVirtualFunctionProposal);
|
QCOMPARE(finalVirtualSymbolResults, expectedVirtualFunctionProposal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user