forked from qt-creator/qt-creator
ClangCodeModel: Implement following a symbol to its type
Making use of LSP's "Go To Type Definition". Just the backend for now, UI to follow. Change-Id: Id73b2cf701eab03913477f6d4d3093e257e80dbd Reviewed-by: David Schulz <david.schulz@qt.io> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
@@ -822,6 +822,7 @@ void ClangdClient::followSymbol(TextDocument *document,
|
||||
CppEditor::CppEditorWidget *editorWidget,
|
||||
const Utils::LinkHandler &callback,
|
||||
bool resolveTarget,
|
||||
FollowTo followTo,
|
||||
bool openInSplit
|
||||
)
|
||||
{
|
||||
@@ -839,7 +840,7 @@ void ClangdClient::followSymbol(TextDocument *document,
|
||||
qCDebug(clangdLog) << "follow symbol requested" << document->filePath()
|
||||
<< adjustedCursor.blockNumber() << adjustedCursor.positionInBlock();
|
||||
d->followSymbol = new ClangdFollowSymbol(this, adjustedCursor, editorWidget, document, callback,
|
||||
openInSplit);
|
||||
followTo, openInSplit);
|
||||
connect(d->followSymbol, &ClangdFollowSymbol::done, this, [this] {
|
||||
d->followSymbol->deleteLater();
|
||||
d->followSymbol = nullptr;
|
||||
|
||||
@@ -34,6 +34,8 @@ Q_DECLARE_LOGGING_CATEGORY(clangdLogAst);
|
||||
|
||||
void setupClangdConfigFile();
|
||||
|
||||
enum class FollowTo { SymbolDef, SymbolType };
|
||||
|
||||
class ClangdClient : public LanguageClient::Client
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -55,6 +57,7 @@ public:
|
||||
CppEditor::CppEditorWidget *editorWidget,
|
||||
const Utils::LinkHandler &callback,
|
||||
bool resolveTarget,
|
||||
FollowTo followTo,
|
||||
bool openInSplit);
|
||||
|
||||
void switchDeclDef(TextEditor::TextDocument *document,
|
||||
|
||||
@@ -83,6 +83,7 @@ public:
|
||||
docRevision(editorWidget ? editorWidget->textDocument()->document()->revision() : -1),
|
||||
openInSplit(openInSplit) {}
|
||||
|
||||
void goToTypeDefinition();
|
||||
void handleGotoDefinitionResult();
|
||||
void sendGotoImplementationRequest(const Utils::Link &link);
|
||||
void handleGotoImplementationResult(const GotoImplementationRequest::Response &response);
|
||||
@@ -118,7 +119,7 @@ public:
|
||||
|
||||
ClangdFollowSymbol::ClangdFollowSymbol(ClangdClient *client, const QTextCursor &cursor,
|
||||
CppEditorWidget *editorWidget, TextDocument *document, const LinkHandler &callback,
|
||||
bool openInSplit)
|
||||
FollowTo followTo, bool openInSplit)
|
||||
: QObject(client),
|
||||
d(new Private(this, client, cursor, editorWidget, document->filePath(), callback,
|
||||
openInSplit))
|
||||
@@ -133,6 +134,11 @@ ClangdFollowSymbol::ClangdFollowSymbol(ClangdClient *client, const QTextCursor &
|
||||
d->focusChangedConnection = connect(qApp, &QApplication::focusChanged,
|
||||
this, [this] { emitDone(); }, Qt::QueuedConnection);
|
||||
|
||||
if (followTo == FollowTo::SymbolType) {
|
||||
d->goToTypeDefinition();
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 1: Follow the symbol via "Go to Definition". At the same time, request the
|
||||
// AST node corresponding to the cursor position, so we can find out whether
|
||||
// we have to look for overrides.
|
||||
@@ -353,6 +359,30 @@ ClangdFollowSymbol::VirtualFunctionAssistProvider::createProcessor(const AssistI
|
||||
= new VirtualFunctionAssistProcessor(m_followSymbol);
|
||||
}
|
||||
|
||||
void ClangdFollowSymbol::Private::goToTypeDefinition()
|
||||
{
|
||||
GotoTypeDefinitionRequest req(TextDocumentPositionParams(TextDocumentIdentifier{uri},
|
||||
Position(cursor)));
|
||||
req.setResponseCallback([sentinel = QPointer(q), this, reqId = req.id()]
|
||||
(const GotoTypeDefinitionRequest::Response &response) {
|
||||
qCDebug(clangdLog) << "received go to type definition reply";
|
||||
if (!sentinel)
|
||||
return;
|
||||
Link link;
|
||||
if (const std::optional<GotoResult> &result = response.result()) {
|
||||
if (const auto ploc = std::get_if<Location>(&*result)) {
|
||||
link = {ploc->toLink()};
|
||||
} else if (const auto plloc = std::get_if<QList<Location>>(&*result)) {
|
||||
if (!plloc->empty())
|
||||
link = plloc->first().toLink();
|
||||
}
|
||||
}
|
||||
q->emitDone(link);
|
||||
});
|
||||
client->sendMessage(req, ClangdClient::SendDocUpdates::Ignore);
|
||||
qCDebug(clangdLog) << "sending go to type definition request";
|
||||
}
|
||||
|
||||
void ClangdFollowSymbol::Private::handleGotoDefinitionResult()
|
||||
{
|
||||
QTC_ASSERT(defLink.hasValidTarget(), return);
|
||||
|
||||
@@ -17,6 +17,7 @@ QT_END_NAMESPACE
|
||||
namespace ClangCodeModel::Internal {
|
||||
class ClangdAstNode;
|
||||
class ClangdClient;
|
||||
enum class FollowTo;
|
||||
|
||||
class ClangdFollowSymbol : public QObject
|
||||
{
|
||||
@@ -25,7 +26,7 @@ public:
|
||||
ClangdFollowSymbol(ClangdClient *client, const QTextCursor &cursor,
|
||||
CppEditor::CppEditorWidget *editorWidget,
|
||||
TextEditor::TextDocument *document, const Utils::LinkHandler &callback,
|
||||
bool openInSplit);
|
||||
FollowTo followTo, bool openInSplit);
|
||||
~ClangdFollowSymbol();
|
||||
void clear();
|
||||
|
||||
|
||||
@@ -169,7 +169,7 @@ void ClangdSwitchDeclDef::Private::handleDeclDefSwitchReplies()
|
||||
const QTextCursor funcNameCursor = cursorForFunctionName(*functionNode);
|
||||
if (!funcNameCursor.isNull()) {
|
||||
client->followSymbol(document.data(), funcNameCursor, editorWidget, callback,
|
||||
true, false);
|
||||
true, FollowTo::SymbolDef, false);
|
||||
}
|
||||
q->emitDone();
|
||||
}
|
||||
|
||||
@@ -262,7 +262,7 @@ void ClangModelManagerSupport::followSymbol(const CppEditor::CursorInEditor &dat
|
||||
if (ClangdClient * const client = clientForFile(data.filePath());
|
||||
client && client->isFullyIndexed()) {
|
||||
client->followSymbol(data.textDocument(), data.cursor(), data.editorWidget(),
|
||||
processLinkCallback, resolveTarget, inNextSplit);
|
||||
processLinkCallback, resolveTarget, FollowTo::SymbolDef, inNextSplit);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -313,38 +313,50 @@ void ClangdTestFollowSymbol::test_data()
|
||||
QTest::addColumn<QString>("targetFile");
|
||||
QTest::addColumn<int>("targetLine");
|
||||
QTest::addColumn<int>("targetColumn");
|
||||
QTest::addColumn<bool>("goToType");
|
||||
|
||||
QTest::newRow("on namespace") << "main.cpp" << 27 << 1 << "header.h" << 28 << 11;
|
||||
QTest::newRow("class ref") << "main.cpp" << 27 << 9 << "header.h" << 34 << 7;
|
||||
QTest::newRow("forward decl (same file)") << "header.h" << 32 << 7 << "header.h" << 34 << 7;
|
||||
QTest::newRow("forward decl (different file") << "header.h" << 48 << 9 << "main.cpp" << 54 << 7;
|
||||
QTest::newRow("class definition (same file)") << "header.h" << 34 << 7 << "header.h" << 32 << 7;
|
||||
QTest::newRow("on namespace") << "main.cpp" << 27 << 1 << "header.h" << 28 << 11 << false;
|
||||
QTest::newRow("class ref") << "main.cpp" << 27 << 9 << "header.h" << 34 << 7 << false;
|
||||
QTest::newRow("forward decl (same file)") << "header.h" << 32 << 7 << "header.h" << 34 << 7
|
||||
<< false;
|
||||
QTest::newRow("forward decl (different file") << "header.h" << 48 << 9 << "main.cpp" << 54 << 7
|
||||
<< false;
|
||||
QTest::newRow("class definition (same file)") << "header.h" << 34 << 7 << "header.h" << 32 << 7
|
||||
<< false;
|
||||
QTest::newRow("class definition (different file)") << "main.cpp" << 54 << 7
|
||||
<< "header.h" << 48 << 7;
|
||||
QTest::newRow("constructor decl") << "header.h" << 36 << 5 << "main.cpp" << 27 << 14;
|
||||
QTest::newRow("constructor definition") << "main.cpp" << 27 << 14 << "header.h" << 36 << 5;
|
||||
QTest::newRow("member ref") << "main.cpp" << 39 << 10 << "header.h" << 38 << 18;
|
||||
QTest::newRow("union member ref") << "main.cpp" << 91 << 20 << "main.cpp" << 86 << 13;
|
||||
QTest::newRow("member decl") << "header.h" << 38 << 18 << "header.h" << 38 << 18;
|
||||
QTest::newRow("function ref") << "main.cpp" << 66 << 12 << "main.cpp" << 35 << 5;
|
||||
QTest::newRow("member function ref") << "main.cpp" << 42 << 12 << "main.cpp" << 49 << 21;
|
||||
QTest::newRow("function with no def ref") << "main.cpp" << 43 << 5 << "header.h" << 59 << 5;
|
||||
QTest::newRow("function def") << "main.cpp" << 35 << 5 << "header.h" << 52 << 5;
|
||||
QTest::newRow("member function def") << "main.cpp" << 49 << 21 << "header.h" << 43 << 9;
|
||||
QTest::newRow("member function decl") << "header.h" << 43 << 9 << "main.cpp" << 49 << 21;
|
||||
QTest::newRow("include") << "main.cpp" << 25 << 13 << "header.h" << 1 << 1;
|
||||
QTest::newRow("local var") << "main.cpp" << 39 << 6 << "main.cpp" << 36 << 9;
|
||||
QTest::newRow("alias") << "main.cpp" << 36 << 5 << "main.cpp" << 33 << 7;
|
||||
QTest::newRow("static var") << "main.cpp" << 40 << 27 << "header.h" << 30 << 7;
|
||||
<< "header.h" << 48 << 7 << false;
|
||||
QTest::newRow("constructor decl") << "header.h" << 36 << 5 << "main.cpp" << 27 << 14 << false;
|
||||
QTest::newRow("constructor definition") << "main.cpp" << 27 << 14 << "header.h" << 36 << 5
|
||||
<< false;
|
||||
QTest::newRow("member ref") << "main.cpp" << 39 << 10 << "header.h" << 38 << 18 << false;
|
||||
QTest::newRow("union member ref") << "main.cpp" << 91 << 20 << "main.cpp" << 86 << 13 << false;
|
||||
QTest::newRow("member decl") << "header.h" << 38 << 18 << "header.h" << 38 << 18 << false;
|
||||
QTest::newRow("function ref") << "main.cpp" << 66 << 12 << "main.cpp" << 35 << 5 << false;
|
||||
QTest::newRow("member function ref") << "main.cpp" << 42 << 12 << "main.cpp" << 49 << 21
|
||||
<< false;
|
||||
QTest::newRow("function with no def ref") << "main.cpp" << 43 << 5 << "header.h" << 59 << 5
|
||||
<< false;
|
||||
QTest::newRow("function def") << "main.cpp" << 35 << 5 << "header.h" << 52 << 5 << false;
|
||||
QTest::newRow("member function def") << "main.cpp" << 49 << 21 << "header.h" << 43 << 9
|
||||
<< false;
|
||||
QTest::newRow("member function decl") << "header.h" << 43 << 9 << "main.cpp" << 49 << 21
|
||||
<< false;
|
||||
QTest::newRow("include") << "main.cpp" << 25 << 13 << "header.h" << 1 << 1 << false;
|
||||
QTest::newRow("local var") << "main.cpp" << 39 << 6 << "main.cpp" << 36 << 9 << false;
|
||||
QTest::newRow("alias") << "main.cpp" << 36 << 5 << "main.cpp" << 33 << 7 << false;
|
||||
QTest::newRow("static var") << "main.cpp" << 40 << 27 << "header.h" << 30 << 7 << false;
|
||||
QTest::newRow("member function ref (other class)") << "main.cpp" << 62 << 39
|
||||
<< "cursor.cpp" << 104 << 22;
|
||||
QTest::newRow("macro ref") << "main.cpp" << 66 << 43 << "header.h" << 27 << 9;
|
||||
QTest::newRow("on namespace 2") << "main.cpp" << 27 << 3 << "header.h" << 28 << 11;
|
||||
QTest::newRow("after namespace") << "main.cpp" << 27 << 7 << "header.h" << 28 << 11;
|
||||
QTest::newRow("operator def") << "main.cpp" << 76 << 13 << "main.cpp" << 72 << 9;
|
||||
QTest::newRow("operator def 2") << "main.cpp" << 80 << 15 << "main.cpp" << 73 << 10;
|
||||
QTest::newRow("operator decl") << "main.cpp" << 72 << 12 << "main.cpp" << 76 << 10;
|
||||
QTest::newRow("operator decl 2") << "main.cpp" << 73 << 12 << "main.cpp" << 80 << 11;
|
||||
<< "cursor.cpp" << 104 << 22 << false;
|
||||
QTest::newRow("macro ref") << "main.cpp" << 66 << 43 << "header.h" << 27 << 9 << false;
|
||||
QTest::newRow("on namespace 2") << "main.cpp" << 27 << 3 << "header.h" << 28 << 11 << false;
|
||||
QTest::newRow("after namespace") << "main.cpp" << 27 << 7 << "header.h" << 28 << 11 << false;
|
||||
QTest::newRow("operator def") << "main.cpp" << 76 << 13 << "main.cpp" << 72 << 9 << false;
|
||||
QTest::newRow("operator def 2") << "main.cpp" << 80 << 15 << "main.cpp" << 73 << 10 << false;
|
||||
QTest::newRow("operator decl") << "main.cpp" << 72 << 12 << "main.cpp" << 76 << 10 << false;
|
||||
QTest::newRow("operator decl 2") << "main.cpp" << 73 << 12 << "main.cpp" << 80 << 11 << false;
|
||||
|
||||
QTest::newRow("go to typedef") << "main.cpp" << 100 << 19 << "main.cpp" << 33 << 7 << true;
|
||||
QTest::newRow("go to type") << "main.cpp" << 101 << 19 << "main.cpp" << 69 << 7 << true;
|
||||
}
|
||||
|
||||
void ClangdTestFollowSymbol::test()
|
||||
@@ -355,6 +367,7 @@ void ClangdTestFollowSymbol::test()
|
||||
QFETCH(QString, targetFile);
|
||||
QFETCH(int, targetLine);
|
||||
QFETCH(int, targetColumn);
|
||||
QFETCH(bool, goToType);
|
||||
|
||||
TextEditor::TextDocument * const doc = document(sourceFile);
|
||||
QVERIFY(doc);
|
||||
@@ -371,7 +384,8 @@ void ClangdTestFollowSymbol::test()
|
||||
QTextCursor cursor(doc->document());
|
||||
const int pos = Utils::Text::positionInText(doc->document(), sourceLine, sourceColumn);
|
||||
cursor.setPosition(pos);
|
||||
client()->followSymbol(doc, cursor, nullptr, handler, true, false);
|
||||
client()->followSymbol(doc, cursor, nullptr, handler, true,
|
||||
goToType ? FollowTo::SymbolType : FollowTo::SymbolDef, false);
|
||||
timer.start(10000);
|
||||
loop.exec();
|
||||
QVERIFY(timer.isActive());
|
||||
|
||||
@@ -92,4 +92,10 @@ struct S {
|
||||
int i = 42;
|
||||
return i;
|
||||
}
|
||||
|
||||
YYY getYYY() const { return YYY(); }
|
||||
Bar getBar() const { return Bar(); }
|
||||
};
|
||||
|
||||
static const auto yyyVar = S().getYYY();
|
||||
static const auto barVar = S().getBar();
|
||||
|
||||
Reference in New Issue
Block a user