ClangCodeModel: Take changes from header files into account immediately

... in the dependent sources. That's also how the built-in code model
behaves, but clangd itself only does this when a document is saved.

Change-Id: I52d6badb0b7f063e5924c05dbf83a6e9849c9f6f
Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
Christian Kandeler
2023-08-22 14:10:22 +02:00
parent 73c5cf29c4
commit b33b99e2d9
13 changed files with 82 additions and 1 deletions

View File

@@ -90,6 +90,7 @@ void ClangCodeModelPlugin::initialize()
addTest<Tests::ClangdTestFindReferences>(); addTest<Tests::ClangdTestFindReferences>();
addTest<Tests::ClangdTestFollowSymbol>(); addTest<Tests::ClangdTestFollowSymbol>();
addTest<Tests::ClangdTestHighlighting>(); addTest<Tests::ClangdTestHighlighting>();
addTest<Tests::ClangdTestIndirectChanges>();
addTest<Tests::ClangdTestLocalReferences>(); addTest<Tests::ClangdTestLocalReferences>();
addTest<Tests::ClangdTestTooltips>(); addTest<Tests::ClangdTestTooltips>();
addTest<Tests::ClangFixItTest>(); addTest<Tests::ClangFixItTest>();

View File

@@ -3,6 +3,7 @@
#include "clangeditordocumentprocessor.h" #include "clangeditordocumentprocessor.h"
#include "clangdclient.h"
#include "clangmodelmanagersupport.h" #include "clangmodelmanagersupport.h"
#include <cppeditor/builtincursorinfo.h> #include <cppeditor/builtincursorinfo.h>
@@ -15,6 +16,8 @@
#include <cppeditor/cppworkingcopy.h> #include <cppeditor/cppworkingcopy.h>
#include <cppeditor/editordocumenthandle.h> #include <cppeditor/editordocumenthandle.h>
#include <languageclient/languageclientmanager.h>
#include <texteditor/fontsettings.h> #include <texteditor/fontsettings.h>
#include <texteditor/texteditor.h> #include <texteditor/texteditor.h>
#include <texteditor/texteditorconstants.h> #include <texteditor/texteditorconstants.h>
@@ -77,5 +80,13 @@ ClangEditorDocumentProcessor *ClangEditorDocumentProcessor::get(const Utils::Fil
CppEditor::CppModelManager::cppEditorDocumentProcessor(filePath)); CppEditor::CppModelManager::cppEditorDocumentProcessor(filePath));
} }
void ClangEditorDocumentProcessor::forceUpdate(TextEditor::TextDocument *doc)
{
if (const auto client = qobject_cast<ClangdClient *>(
LanguageClient::LanguageClientManager::clientForDocument(doc))) {
client->documentContentsChanged(doc, 0, 0, 0);
}
}
} // namespace Internal } // namespace Internal
} // namespace ClangCodeModel } // namespace ClangCodeModel

View File

@@ -22,7 +22,6 @@ public:
void setParserConfig(const CppEditor::BaseEditorDocumentParser::Configuration &config) override; void setParserConfig(const CppEditor::BaseEditorDocumentParser::Configuration &config) override;
CppEditor::BaseEditorDocumentParser::Configuration parserConfig(); CppEditor::BaseEditorDocumentParser::Configuration parserConfig();
public:
static ClangEditorDocumentProcessor *get(const Utils::FilePath &filePath); static ClangEditorDocumentProcessor *get(const Utils::FilePath &filePath);
signals: signals:
@@ -30,6 +29,8 @@ signals:
const CppEditor::BaseEditorDocumentParser::Configuration &config); const CppEditor::BaseEditorDocumentParser::Configuration &config);
private: private:
void forceUpdate(TextEditor::TextDocument *doc) override;
TextEditor::TextDocument &m_document; TextEditor::TextDocument &m_document;
}; };

View File

@@ -2061,6 +2061,47 @@ void ClangdTestExternalChanges::test()
QVERIFY(waitForSignalOrTimeout(newClient, &ClangdClient::textMarkCreated, timeOutInMs())); QVERIFY(waitForSignalOrTimeout(newClient, &ClangdClient::textMarkCreated, timeOutInMs()));
} }
ClangdTestIndirectChanges::ClangdTestIndirectChanges()
{
setProjectFileName("indirect-changes.pro");
setSourceFileNames({"main.cpp", "directheader.h", "indirectheader.h", "unrelatedheader.h"});
}
void ClangdTestIndirectChanges::test()
{
// Initially, everything is fine.
const TextDocument * const src = document("main.cpp");
QVERIFY(src);
QVERIFY(src->marks().isEmpty());
// Write into an indirectly included header file. Our source file should have diagnostics now.
const TextDocument * const indirectHeader = document("indirectheader.h");
QVERIFY(indirectHeader);
QTextCursor cursor(indirectHeader->document());
cursor.insertText("blubb");
while (src->marks().isEmpty())
QVERIFY(waitForSignalOrTimeout(client(), &ClangdClient::textMarkCreated, timeOutInMs()));
// Remove the inserted text again; the diagnostics should disappear.
cursor.document()->undo();
QVERIFY(cursor.document()->toPlainText().isEmpty());
while (!src->marks().isEmpty()) {
QVERIFY(waitForSignalOrTimeout(client(), &ClangdClient::highlightingResultsReady,
timeOutInMs()));
}
// Now write into a header file that is not included anywhere.
// We expect diagnostics only for the header itself.
const TextDocument * const unrelatedHeader = document("unrelatedheader.h");
QVERIFY(indirectHeader);
QTextCursor cursor2(unrelatedHeader->document());
cursor2.insertText("blubb");
while (waitForSignalOrTimeout(client(), &ClangdClient::textMarkCreated, timeOutInMs()))
;
QVERIFY(!unrelatedHeader->marks().isEmpty());
QVERIFY(src->marks().isEmpty());
}
} // namespace Tests } // namespace Tests
} // namespace Internal } // namespace Internal
} // namespace ClangCodeModel } // namespace ClangCodeModel

View File

@@ -188,6 +188,17 @@ private slots:
void test(); void test();
}; };
class ClangdTestIndirectChanges : public ClangdTest
{
Q_OBJECT
public:
ClangdTestIndirectChanges();
private slots:
void test();
};
} // namespace Tests } // namespace Tests
} // namespace Internal } // namespace Internal
} // namespace ClangCodeModel } // namespace ClangCodeModel

View File

@@ -60,5 +60,10 @@
<file>fixits/diagnostic_comparison_fixit.cpp</file> <file>fixits/diagnostic_comparison_fixit.cpp</file>
<file>fixits/diagnostic_semicolon_fixit_expected.cpp</file> <file>fixits/diagnostic_semicolon_fixit_expected.cpp</file>
<file>fixits/diagnostic_semicolon_fixit.cpp</file> <file>fixits/diagnostic_semicolon_fixit.cpp</file>
<file>indirect-changes/directheader.h</file>
<file>indirect-changes/indirect-changes.pro</file>
<file>indirect-changes/indirectheader.h</file>
<file>indirect-changes/main.cpp</file>
<file>indirect-changes/unrelatedheader.h</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@@ -0,0 +1 @@
#include "indirectheader.h"

View File

@@ -0,0 +1,4 @@
CONFIG -= qt
SOURCES = main.cpp
HEADERS = directheader.h indirectheader.h unrelatedheader.h

View File

@@ -0,0 +1,3 @@
#include "directheader.h"
int main() {}

View File

@@ -283,6 +283,7 @@ void BuiltinEditorDocumentProcessor::onParserFinished(CPlusPlus::Document::Ptr d
if (!cppDoc->includedFiles().contains(document->filePath())) if (!cppDoc->includedFiles().contains(document->filePath()))
continue; continue;
cppEditorDoc->scheduleProcessDocument(); cppEditorDoc->scheduleProcessDocument();
forceUpdate(cppEditorDoc);
} }
} }

View File

@@ -43,6 +43,8 @@ private:
SemanticInfo::Source createSemanticInfoSource(bool force) const; SemanticInfo::Source createSemanticInfoSource(bool force) const;
virtual void forceUpdate(TextEditor::TextDocument *) {}
private: private:
BuiltinEditorDocumentParser::Ptr m_parser; BuiltinEditorDocumentParser::Ptr m_parser;
QFuture<void> m_parserFuture; QFuture<void> m_parserFuture;