[clang-tools-extra] 2cd33e6 - [clangd] Track document versions, include them with diags, enhance logs
Sam McCall via cfe-commits
cfe-commits at lists.llvm.org
Wed Mar 4 16:23:18 PST 2020
Author: Sam McCall
Date: 2020-03-05T01:22:32+01:00
New Revision: 2cd33e6fe60f1fe1155ae86ef7e843df55066bda
URL: https://github.com/llvm/llvm-project/commit/2cd33e6fe60f1fe1155ae86ef7e843df55066bda
DIFF: https://github.com/llvm/llvm-project/commit/2cd33e6fe60f1fe1155ae86ef7e843df55066bda.diff
LOG: [clangd] Track document versions, include them with diags, enhance logs
Summary:
This ties to an LSP feature (diagnostic versioning) but really a lot
of the value is in being able to log what's happening with file versions
and queues more descriptively and clearly.
As such it's fairly invasive, for a logging patch :-\
Key decisions:
- at the LSP layer, we don't reqire the client to provide versions (LSP
makes it mandatory but we never enforced it). If not provided,
versions start at 0 and increment. DraftStore handles this.
- don't propagate magically using contexts, but rather manually:
addDocument -> ParseInputs -> (ParsedAST, Preamble, various callbacks)
Context-propagation would hide the versions from ClangdServer, which
would make producing good log messages hard
- within ClangdServer, treat versions as opaque and unordered.
std::string is a convenient type for this, and allows richer versions
for embedders. They're "mandatory" but "null" is a reasonable default.
Subscribers: ilya-biryukov, javed.absar, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D75582
Added:
clang-tools-extra/clangd/test/version.test
Modified:
clang-tools-extra/clangd/ClangdLSPServer.cpp
clang-tools-extra/clangd/ClangdLSPServer.h
clang-tools-extra/clangd/ClangdServer.cpp
clang-tools-extra/clangd/ClangdServer.h
clang-tools-extra/clangd/Compiler.h
clang-tools-extra/clangd/ParsedAST.cpp
clang-tools-extra/clangd/ParsedAST.h
clang-tools-extra/clangd/Preamble.cpp
clang-tools-extra/clangd/Preamble.h
clang-tools-extra/clangd/Protocol.cpp
clang-tools-extra/clangd/Protocol.h
clang-tools-extra/clangd/TUScheduler.cpp
clang-tools-extra/clangd/TUScheduler.h
clang-tools-extra/clangd/index/FileIndex.cpp
clang-tools-extra/clangd/index/FileIndex.h
clang-tools-extra/clangd/test/diagnostic-category.test
clang-tools-extra/clangd/test/diagnostics-no-tidy.test
clang-tools-extra/clangd/test/diagnostics-notes.test
clang-tools-extra/clangd/test/diagnostics.test
clang-tools-extra/clangd/test/did-change-configuration-params.test
clang-tools-extra/clangd/test/execute-command.test
clang-tools-extra/clangd/test/fixits-codeaction.test
clang-tools-extra/clangd/test/fixits-command.test
clang-tools-extra/clangd/test/fixits-embed-in-diagnostic.test
clang-tools-extra/clangd/test/path-mappings.test
clang-tools-extra/clangd/test/semantic-highlighting.test
clang-tools-extra/clangd/unittests/ClangdTests.cpp
clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
clang-tools-extra/clangd/unittests/FileIndexTests.cpp
clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp
clang-tools-extra/clangd/unittests/SyncAPI.cpp
clang-tools-extra/clangd/unittests/SyncAPI.h
clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp
clang-tools-extra/clangd/unittests/TestTU.cpp
clang-tools-extra/clangd/unittests/XRefsTests.cpp
Removed:
################################################################################
diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp
index 44288eb7274d..748269d5aef4 100644
--- a/clang-tools-extra/clangd/ClangdLSPServer.cpp
+++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp
@@ -41,6 +41,21 @@
namespace clang {
namespace clangd {
namespace {
+
+// LSP defines file versions as numbers that increase.
+// ClangdServer treats them as opaque and therefore uses strings instead.
+std::string encodeVersion(int64_t LSPVersion) {
+ return llvm::to_string(LSPVersion);
+}
+llvm::Optional<int64_t> decodeVersion(llvm::StringRef Encoded) {
+ int64_t Result;
+ if (llvm::to_integer(Encoded, Result, 10))
+ return Result;
+ else if (!Encoded.empty()) // Empty can be e.g. diagnostics on close.
+ elog("unexpected non-numeric version {0}", Encoded);
+ return llvm::None;
+}
+
/// Transforms a tweak into a code action that would apply it if executed.
/// EXPECTS: T.prepare() was called and returned true.
CodeAction toCodeAction(const ClangdServer::TweakRef &T, const URIForFile &File,
@@ -630,8 +645,9 @@ void ClangdLSPServer::onDocumentDidOpen(
const std::string &Contents = Params.textDocument.text;
- DraftMgr.addDraft(File, Params.textDocument.version, Contents);
- Server->addDocument(File, Contents, WantDiagnostics::Yes);
+ auto Version = DraftMgr.addDraft(File, Params.textDocument.version, Contents);
+ Server->addDocument(File, Contents, encodeVersion(Version),
+ WantDiagnostics::Yes);
}
void ClangdLSPServer::onDocumentDidChange(
@@ -654,7 +670,8 @@ void ClangdLSPServer::onDocumentDidChange(
return;
}
- Server->addDocument(File, Draft->Contents, WantDiags, Params.forceRebuild);
+ Server->addDocument(File, Draft->Contents, encodeVersion(Draft->Version),
+ WantDiags, Params.forceRebuild);
}
void ClangdLSPServer::onFileEvent(const DidChangeWatchedFilesParams &Params) {
@@ -1347,7 +1364,8 @@ bool ClangdLSPServer::shouldRunCompletion(
}
void ClangdLSPServer::onHighlightingsReady(
- PathRef File, std::vector<HighlightingToken> Highlightings) {
+ PathRef File, llvm::StringRef Version,
+ std::vector<HighlightingToken> Highlightings) {
std::vector<HighlightingToken> Old;
std::vector<HighlightingToken> HighlightingsCopy = Highlightings;
{
@@ -1358,14 +1376,18 @@ void ClangdLSPServer::onHighlightingsReady(
// LSP allows us to send incremental edits of highlightings. Also need to
diff
// to remove highlightings from tokens that should no longer have them.
std::vector<LineHighlightings> Diffed =
diff Highlightings(Highlightings, Old);
- publishSemanticHighlighting(
- {{URIForFile::canonicalize(File, /*TUPath=*/File)},
- toSemanticHighlightingInformation(Diffed)});
+ SemanticHighlightingParams Notification;
+ Notification.TextDocument.uri =
+ URIForFile::canonicalize(File, /*TUPath=*/File);
+ Notification.TextDocument.version = decodeVersion(Version);
+ Notification.Lines = toSemanticHighlightingInformation(Diffed);
+ publishSemanticHighlighting(Notification);
}
-void ClangdLSPServer::onDiagnosticsReady(PathRef File,
+void ClangdLSPServer::onDiagnosticsReady(PathRef File, llvm::StringRef Version,
std::vector<Diag> Diagnostics) {
PublishDiagnosticsParams Notification;
+ Notification.version = decodeVersion(Version);
Notification.uri = URIForFile::canonicalize(File, /*TUPath=*/File);
DiagnosticToReplacementMap LocalFixIts; // Temporary storage
for (auto &Diag : Diagnostics) {
@@ -1475,8 +1497,10 @@ void ClangdLSPServer::reparseOpenedFiles(
// Reparse only opened files that were modified.
for (const Path &FilePath : DraftMgr.getActiveFiles())
if (ModifiedFiles.find(FilePath) != ModifiedFiles.end())
- Server->addDocument(FilePath, DraftMgr.getDraft(FilePath)->Contents,
- WantDiagnostics::Auto);
+ if (auto Draft = DraftMgr.getDraft(FilePath)) // else disappeared in race?
+ Server->addDocument(FilePath, std::move(Draft->Contents),
+ encodeVersion(Draft->Version),
+ WantDiagnostics::Auto);
}
} // namespace clangd
diff --git a/clang-tools-extra/clangd/ClangdLSPServer.h b/clang-tools-extra/clangd/ClangdLSPServer.h
index 4ab0354ead72..e70b7b56a4f2 100644
--- a/clang-tools-extra/clangd/ClangdLSPServer.h
+++ b/clang-tools-extra/clangd/ClangdLSPServer.h
@@ -21,6 +21,7 @@
#include "clang/Tooling/Core/Replacement.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringSet.h"
+#include "llvm/Support/JSON.h"
#include <memory>
namespace clang {
@@ -57,10 +58,11 @@ class ClangdLSPServer : private ClangdServer::Callbacks {
private:
// Implement ClangdServer::Callbacks.
- void onDiagnosticsReady(PathRef File, std::vector<Diag> Diagnostics) override;
+ void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
+ std::vector<Diag> Diagnostics) override;
void onFileUpdated(PathRef File, const TUStatus &Status) override;
void
- onHighlightingsReady(PathRef File,
+ onHighlightingsReady(PathRef File, llvm::StringRef Version,
std::vector<HighlightingToken> Highlightings) override;
void onBackgroundIndexProgress(const BackgroundQueue::Stats &Stats) override;
diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp
index cd833af13e49..5d2bfa7c8c57 100644
--- a/clang-tools-extra/clangd/ClangdServer.cpp
+++ b/clang-tools-extra/clangd/ClangdServer.cpp
@@ -62,11 +62,11 @@ struct UpdateIndexCallbacks : public ParsingCallbacks {
: FIndex(FIndex), ServerCallbacks(ServerCallbacks),
SemanticHighlighting(SemanticHighlighting) {}
- void onPreambleAST(PathRef Path, ASTContext &Ctx,
+ void onPreambleAST(PathRef Path, llvm::StringRef Version, ASTContext &Ctx,
std::shared_ptr<clang::Preprocessor> PP,
const CanonicalIncludes &CanonIncludes) override {
if (FIndex)
- FIndex->updatePreamble(Path, Ctx, std::move(PP), CanonIncludes);
+ FIndex->updatePreamble(Path, Version, Ctx, std::move(PP), CanonIncludes);
}
void onMainAST(PathRef Path, ParsedAST &AST, PublishFn Publish) override {
@@ -80,16 +80,19 @@ struct UpdateIndexCallbacks : public ParsingCallbacks {
if (ServerCallbacks)
Publish([&]() {
- ServerCallbacks->onDiagnosticsReady(Path, std::move(Diagnostics));
+ ServerCallbacks->onDiagnosticsReady(Path, AST.version(),
+ std::move(Diagnostics));
if (SemanticHighlighting)
- ServerCallbacks->onHighlightingsReady(Path, std::move(Highlightings));
+ ServerCallbacks->onHighlightingsReady(Path, AST.version(),
+ std::move(Highlightings));
});
}
- void onFailedAST(PathRef Path, std::vector<Diag> Diags,
- PublishFn Publish) override {
+ void onFailedAST(PathRef Path, llvm::StringRef Version,
+ std::vector<Diag> Diags, PublishFn Publish) override {
if (ServerCallbacks)
- Publish([&]() { ServerCallbacks->onDiagnosticsReady(Path, Diags); });
+ Publish(
+ [&]() { ServerCallbacks->onDiagnosticsReady(Path, Version, Diags); });
}
void onFileUpdated(PathRef File, const TUStatus &Status) override {
@@ -169,6 +172,7 @@ ClangdServer::ClangdServer(const GlobalCompilationDatabase &CDB,
}
void ClangdServer::addDocument(PathRef File, llvm::StringRef Contents,
+ llvm::StringRef Version,
WantDiagnostics WantDiags, bool ForceRebuild) {
auto FS = FSProvider.getFileSystem();
@@ -183,6 +187,7 @@ void ClangdServer::addDocument(PathRef File, llvm::StringRef Contents,
ParseInputs Inputs;
Inputs.FS = FS;
Inputs.Contents = std::string(Contents);
+ Inputs.Version = Version.str();
Inputs.ForceRebuild = ForceRebuild;
Inputs.Opts = std::move(Opts);
Inputs.Index = Index;
diff --git a/clang-tools-extra/clangd/ClangdServer.h b/clang-tools-extra/clangd/ClangdServer.h
index d098f6242f72..4c3fe56dd7e2 100644
--- a/clang-tools-extra/clangd/ClangdServer.h
+++ b/clang-tools-extra/clangd/ClangdServer.h
@@ -69,7 +69,7 @@ class ClangdServer {
/// Called by ClangdServer when \p Diagnostics for \p File are ready.
/// May be called concurrently for separate files, not for a single file.
- virtual void onDiagnosticsReady(PathRef File,
+ virtual void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
std::vector<Diag> Diagnostics) {}
/// Called whenever the file status is updated.
/// May be called concurrently for separate files, not for a single file.
@@ -78,7 +78,7 @@ class ClangdServer {
/// Called by ClangdServer when some \p Highlightings for \p File are ready.
/// May be called concurrently for separate files, not for a single file.
virtual void
- onHighlightingsReady(PathRef File,
+ onHighlightingsReady(PathRef File, llvm::StringRef Version,
std::vector<HighlightingToken> Highlightings) {}
/// Called when background indexing tasks are enqueued/started/completed.
@@ -171,13 +171,17 @@ class ClangdServer {
/// \p File is already tracked. Also schedules parsing of the AST for it on a
/// separate thread. When the parsing is complete, DiagConsumer passed in
/// constructor will receive onDiagnosticsReady callback.
+ /// Version identifies this snapshot and is propagated to ASTs, preambles,
+ /// diagnostics etc built from it.
void addDocument(PathRef File, StringRef Contents,
+ llvm::StringRef Version = "null",
WantDiagnostics WD = WantDiagnostics::Auto,
bool ForceRebuild = false);
/// Remove \p File from list of tracked files, schedule a request to free
/// resources associated with it. Pending diagnostics for closed files may not
/// be delivered, even if requested with WantDiags::Auto or WantDiags::Yes.
+ /// An empty set of diagnostics will be delivered, with Version = "".
void removeDocument(PathRef File);
/// Run code completion for \p File at \p Pos.
diff --git a/clang-tools-extra/clangd/Compiler.h b/clang-tools-extra/clangd/Compiler.h
index 356293b158f8..ef5386bb0d17 100644
--- a/clang-tools-extra/clangd/Compiler.h
+++ b/clang-tools-extra/clangd/Compiler.h
@@ -45,6 +45,8 @@ struct ParseInputs {
tooling::CompileCommand CompileCommand;
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS;
std::string Contents;
+ // Version identifier for Contents, provided by the client and opaque to us.
+ std::string Version = "null";
// Prevent reuse of the cached preamble/AST. Slow! Useful to workaround
// clangd's assumption that missing header files will stay missing.
bool ForceRebuild = false;
diff --git a/clang-tools-extra/clangd/ParsedAST.cpp b/clang-tools-extra/clangd/ParsedAST.cpp
index b18c9af077c6..91c71789dbe0 100644
--- a/clang-tools-extra/clangd/ParsedAST.cpp
+++ b/clang-tools-extra/clangd/ParsedAST.cpp
@@ -239,7 +239,8 @@ void dumpAST(ParsedAST &AST, llvm::raw_ostream &OS) {
}
llvm::Optional<ParsedAST>
-ParsedAST::build(std::unique_ptr<clang::CompilerInvocation> CI,
+ParsedAST::build(llvm::StringRef Version,
+ std::unique_ptr<clang::CompilerInvocation> CI,
llvm::ArrayRef<Diag> CompilerInvocationDiags,
std::shared_ptr<const PreambleData> Preamble,
std::unique_ptr<llvm::MemoryBuffer> Buffer,
@@ -427,10 +428,10 @@ ParsedAST::build(std::unique_ptr<clang::CompilerInvocation> CI,
std::vector<Diag> D = ASTDiags.take(CTContext.getPointer());
Diags.insert(Diags.end(), D.begin(), D.end());
}
- return ParsedAST(std::move(Preamble), std::move(Clang), std::move(Action),
- std::move(Tokens), std::move(Macros), std::move(ParsedDecls),
- std::move(Diags), std::move(Includes),
- std::move(CanonIncludes));
+ return ParsedAST(Version, std::move(Preamble), std::move(Clang),
+ std::move(Action), std::move(Tokens), std::move(Macros),
+ std::move(ParsedDecls), std::move(Diags),
+ std::move(Includes), std::move(CanonIncludes));
}
ParsedAST::ParsedAST(ParsedAST &&Other) = default;
@@ -512,14 +513,15 @@ const CanonicalIncludes &ParsedAST::getCanonicalIncludes() const {
return CanonIncludes;
}
-ParsedAST::ParsedAST(std::shared_ptr<const PreambleData> Preamble,
+ParsedAST::ParsedAST(llvm::StringRef Version,
+ std::shared_ptr<const PreambleData> Preamble,
std::unique_ptr<CompilerInstance> Clang,
std::unique_ptr<FrontendAction> Action,
syntax::TokenBuffer Tokens, MainFileMacros Macros,
std::vector<Decl *> LocalTopLevelDecls,
std::vector<Diag> Diags, IncludeStructure Includes,
CanonicalIncludes CanonIncludes)
- : Preamble(std::move(Preamble)), Clang(std::move(Clang)),
+ : Version(Version), Preamble(std::move(Preamble)), Clang(std::move(Clang)),
Action(std::move(Action)), Tokens(std::move(Tokens)),
Macros(std::move(Macros)), Diags(std::move(Diags)),
LocalTopLevelDecls(std::move(LocalTopLevelDecls)),
@@ -546,7 +548,7 @@ buildAST(PathRef FileName, std::unique_ptr<CompilerInvocation> Invocation,
}
return ParsedAST::build(
- std::make_unique<CompilerInvocation>(*Invocation),
+ Inputs.Version, std::make_unique<CompilerInvocation>(*Invocation),
CompilerInvocationDiags, Preamble,
llvm::MemoryBuffer::getMemBufferCopy(Inputs.Contents, FileName),
std::move(VFS), Inputs.Index, Inputs.Opts);
diff --git a/clang-tools-extra/clangd/ParsedAST.h b/clang-tools-extra/clangd/ParsedAST.h
index 6142891482d1..8620f31ff1e5 100644
--- a/clang-tools-extra/clangd/ParsedAST.h
+++ b/clang-tools-extra/clangd/ParsedAST.h
@@ -48,7 +48,7 @@ class ParsedAST {
/// Attempts to run Clang and store parsed AST. If \p Preamble is non-null
/// it is reused during parsing.
static llvm::Optional<ParsedAST>
- build(std::unique_ptr<clang::CompilerInvocation> CI,
+ build(llvm::StringRef Version, std::unique_ptr<clang::CompilerInvocation> CI,
llvm::ArrayRef<Diag> CompilerInvocationDiags,
std::shared_ptr<const PreambleData> Preamble,
std::unique_ptr<llvm::MemoryBuffer> Buffer,
@@ -101,14 +101,19 @@ class ParsedAST {
/// (!) does not have tokens from the preamble.
const syntax::TokenBuffer &getTokens() const { return Tokens; }
+ /// Returns the version of the ParseInputs this AST was built from.
+ llvm::StringRef version() const { return Version; }
+
private:
- ParsedAST(std::shared_ptr<const PreambleData> Preamble,
+ ParsedAST(llvm::StringRef Version,
+ std::shared_ptr<const PreambleData> Preamble,
std::unique_ptr<CompilerInstance> Clang,
std::unique_ptr<FrontendAction> Action, syntax::TokenBuffer Tokens,
MainFileMacros Macros, std::vector<Decl *> LocalTopLevelDecls,
std::vector<Diag> Diags, IncludeStructure Includes,
CanonicalIncludes CanonIncludes);
+ std::string Version;
// In-memory preambles must outlive the AST, it is important that this member
// goes before Clang and Action.
std::shared_ptr<const PreambleData> Preamble;
diff --git a/clang-tools-extra/clangd/Preamble.cpp b/clang-tools-extra/clangd/Preamble.cpp
index f2b6b017f10f..86fa7905a61e 100644
--- a/clang-tools-extra/clangd/Preamble.cpp
+++ b/clang-tools-extra/clangd/Preamble.cpp
@@ -75,12 +75,13 @@ class CppFilePreambleCallbacks : public PreambleCallbacks {
} // namespace
-PreambleData::PreambleData(PrecompiledPreamble Preamble,
+PreambleData::PreambleData(llvm::StringRef Version,
+ PrecompiledPreamble Preamble,
std::vector<Diag> Diags, IncludeStructure Includes,
MainFileMacros Macros,
std::unique_ptr<PreambleFileStatusCache> StatCache,
CanonicalIncludes CanonIncludes)
- : Preamble(std::move(Preamble)), Diags(std::move(Diags)),
+ : Version(Version), Preamble(std::move(Preamble)), Diags(std::move(Diags)),
Includes(std::move(Includes)), Macros(std::move(Macros)),
StatCache(std::move(StatCache)), CanonIncludes(std::move(CanonIncludes)) {
}
@@ -102,12 +103,17 @@ buildPreamble(PathRef FileName, CompilerInvocation &CI,
compileCommandsAreEqual(Inputs.CompileCommand, OldCompileCommand) &&
OldPreamble->Preamble.CanReuse(CI, ContentsBuffer.get(), Bounds,
Inputs.FS.get())) {
- vlog("Reusing preamble for {0}", FileName);
+ vlog("Reusing preamble version {0} for version {1} of {2}",
+ OldPreamble->Version, Inputs.Version, FileName);
return OldPreamble;
}
- vlog(OldPreamble ? "Rebuilding invalidated preamble for {0}"
- : "Building first preamble for {0}",
- FileName);
+ if (OldPreamble)
+ vlog("Rebuilding invalidated preamble for {0} version {1} "
+ "(previous was version {2})",
+ FileName, Inputs.Version, OldPreamble->Version);
+ else
+ vlog("Building first preamble for {0} verson {1}", FileName,
+ Inputs.Version);
trace::Span Tracer("BuildPreamble");
SPAN_ATTACH(Tracer, "File", FileName);
@@ -145,16 +151,17 @@ buildPreamble(PathRef FileName, CompilerInvocation &CI,
CI.getFrontendOpts().SkipFunctionBodies = false;
if (BuiltPreamble) {
- vlog("Built preamble of size {0} for file {1}", BuiltPreamble->getSize(),
- FileName);
+ vlog("Built preamble of size {0} for file {1} version {2}",
+ BuiltPreamble->getSize(), FileName, Inputs.Version);
std::vector<Diag> Diags = PreambleDiagnostics.take();
return std::make_shared<PreambleData>(
- std::move(*BuiltPreamble), std::move(Diags),
+ Inputs.Version, std::move(*BuiltPreamble), std::move(Diags),
SerializedDeclsCollector.takeIncludes(),
SerializedDeclsCollector.takeMacros(), std::move(StatCache),
SerializedDeclsCollector.takeCanonicalIncludes());
} else {
- elog("Could not build a preamble for file {0}", FileName);
+ elog("Could not build a preamble for file {0} version {2}", FileName,
+ Inputs.Version);
return nullptr;
}
}
diff --git a/clang-tools-extra/clangd/Preamble.h b/clang-tools-extra/clangd/Preamble.h
index 55dcce76d2ee..1517daaf05a9 100644
--- a/clang-tools-extra/clangd/Preamble.h
+++ b/clang-tools-extra/clangd/Preamble.h
@@ -43,11 +43,14 @@ namespace clangd {
/// As we must avoid re-parsing the preamble, any information that can only
/// be obtained during parsing must be eagerly captured and stored here.
struct PreambleData {
- PreambleData(PrecompiledPreamble Preamble, std::vector<Diag> Diags,
- IncludeStructure Includes, MainFileMacros Macros,
+ PreambleData(llvm::StringRef Version, PrecompiledPreamble Preamble,
+ std::vector<Diag> Diags, IncludeStructure Includes,
+ MainFileMacros Macros,
std::unique_ptr<PreambleFileStatusCache> StatCache,
CanonicalIncludes CanonIncludes);
+ // Version of the ParseInputs this preamble was built from.
+ std::string Version;
tooling::CompileCommand CompileCommand;
PrecompiledPreamble Preamble;
std::vector<Diag> Diags;
diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp
index ba96f495c148..1975fc191aa1 100644
--- a/clang-tools-extra/clangd/Protocol.cpp
+++ b/clang-tools-extra/clangd/Protocol.cpp
@@ -92,8 +92,7 @@ bool fromJSON(const llvm::json::Value &Params, TextDocumentIdentifier &R) {
llvm::json::Value toJSON(const VersionedTextDocumentIdentifier &R) {
auto Result = toJSON(static_cast<const TextDocumentIdentifier &>(R));
- if (R.version)
- Result.getAsObject()->try_emplace("version", R.version);
+ Result.getAsObject()->try_emplace("version", R.version);
return Result;
}
@@ -547,8 +546,9 @@ bool fromJSON(const llvm::json::Value &Params, Diagnostic &R) {
llvm::json::Value toJSON(const PublishDiagnosticsParams &PDP) {
return llvm::json::Object{
- {"uri", PDP.uri},
- {"diagnostics", PDP.diagnostics},
+ {"uri", PDP.uri},
+ {"diagnostics", PDP.diagnostics},
+ {"version", PDP.version},
};
}
diff --git a/clang-tools-extra/clangd/Protocol.h b/clang-tools-extra/clangd/Protocol.h
index 5d3914da699f..6ab6bcc920b2 100644
--- a/clang-tools-extra/clangd/Protocol.h
+++ b/clang-tools-extra/clangd/Protocol.h
@@ -125,14 +125,16 @@ llvm::json::Value toJSON(const TextDocumentIdentifier &);
bool fromJSON(const llvm::json::Value &, TextDocumentIdentifier &);
struct VersionedTextDocumentIdentifier : public TextDocumentIdentifier {
- // The version number of this document. If a versioned text document
- // identifier is sent from the server to the client and the file is not open
- // in the editor (the server has not received an open notification before) the
- // server can send `null` to indicate that the version is known and the
- // content on disk is the master (as speced with document content ownership).
- //
- // The version number of a document will increase after each change, including
- // undo/redo. The number doesn't need to be consecutive.
+ /// The version number of this document. If a versioned text document
+ /// identifier is sent from the server to the client and the file is not open
+ /// in the editor (the server has not received an open notification before)
+ /// the server can send `null` to indicate that the version is known and the
+ /// content on disk is the master (as speced with document content ownership).
+ ///
+ /// The version number of a document will increase after each change,
+ /// including undo/redo. The number doesn't need to be consecutive.
+ ///
+ /// clangd extension: versions are optional, and synthesized if missing.
llvm::Optional<std::int64_t> version;
};
llvm::json::Value toJSON(const VersionedTextDocumentIdentifier &);
@@ -237,7 +239,10 @@ struct TextDocumentItem {
std::string languageId;
/// The version number of this document (it will strictly increase after each
- std::int64_t version = 0;
+ /// change, including undo/redo.
+ ///
+ /// clangd extension: versions are optional, and synthesized if missing.
+ llvm::Optional<int64_t> version;
/// The content of the opened text document.
std::string text;
@@ -811,6 +816,8 @@ struct PublishDiagnosticsParams {
URIForFile uri;
/// An array of diagnostic information items.
std::vector<Diagnostic> diagnostics;
+ /// The version number of the document the diagnostics are published for.
+ llvm::Optional<int64_t> version;
};
llvm::json::Value toJSON(const PublishDiagnosticsParams &);
@@ -1353,7 +1360,7 @@ llvm::json::Value toJSON(const SemanticHighlightingInformation &Highlighting);
/// Parameters for the semantic highlighting (server-side) push notification.
struct SemanticHighlightingParams {
/// The textdocument these highlightings belong to.
- TextDocumentIdentifier TextDocument;
+ VersionedTextDocumentIdentifier TextDocument;
/// The lines of highlightings that should be sent.
std::vector<SemanticHighlightingInformation> Lines;
};
diff --git a/clang-tools-extra/clangd/TUScheduler.cpp b/clang-tools-extra/clangd/TUScheduler.cpp
index 2ddfae623677..3ce0a5b10d95 100644
--- a/clang-tools-extra/clangd/TUScheduler.cpp
+++ b/clang-tools-extra/clangd/TUScheduler.cpp
@@ -375,7 +375,7 @@ ASTWorker::~ASTWorker() {
}
void ASTWorker::update(ParseInputs Inputs, WantDiagnostics WantDiags) {
- llvm::StringRef TaskName = "Update";
+ std::string TaskName = llvm::formatv("Update ({0})", Inputs.Version);
auto Task = [=]() mutable {
auto RunPublish = [&](llvm::function_ref<void()> Publish) {
// Ensure we only publish results from the worker if the file was not
@@ -409,8 +409,8 @@ void ASTWorker::update(ParseInputs Inputs, WantDiagnostics WantDiags) {
}
RanASTCallback = false;
emitTUStatus({TUAction::BuildingPreamble, TaskName});
- log("Updating file {0} with command {1}\n[{2}]\n{3}", FileName,
- Inputs.CompileCommand.Heuristic,
+ log("ASTWorker building file {0} version {1} with command {2}\n[{3}]\n{4}",
+ FileName, Inputs.Version, Inputs.CompileCommand.Heuristic,
Inputs.CompileCommand.Directory,
llvm::join(Inputs.CompileCommand.CommandLine, " "));
// Rebuild the preamble and the AST.
@@ -431,8 +431,8 @@ void ASTWorker::update(ParseInputs Inputs, WantDiagnostics WantDiags) {
Details.BuildFailed = true;
emitTUStatus({TUAction::BuildingPreamble, TaskName}, &Details);
// Report the diagnostics we collected when parsing the command line.
- Callbacks.onFailedAST(FileName, std::move(CompilerInvocationDiags),
- RunPublish);
+ Callbacks.onFailedAST(FileName, Inputs.Version,
+ std::move(CompilerInvocationDiags), RunPublish);
// Make sure anyone waiting for the preamble gets notified it could not
// be built.
PreambleWasBuilt.notify();
@@ -445,9 +445,11 @@ void ASTWorker::update(ParseInputs Inputs, WantDiagnostics WantDiags) {
std::shared_ptr<const PreambleData> NewPreamble = buildPreamble(
FileName, *Invocation, OldPreamble, OldCommand, Inputs,
StorePreambleInMemory,
- [this](ASTContext &Ctx, std::shared_ptr<clang::Preprocessor> PP,
- const CanonicalIncludes &CanonIncludes) {
- Callbacks.onPreambleAST(FileName, Ctx, std::move(PP), CanonIncludes);
+ [this, Version(Inputs.Version)](
+ ASTContext &Ctx, std::shared_ptr<clang::Preprocessor> PP,
+ const CanonicalIncludes &CanonIncludes) {
+ Callbacks.onPreambleAST(FileName, Version, Ctx, std::move(PP),
+ CanonIncludes);
});
bool CanReuseAST = InputsAreTheSame && (OldPreamble == NewPreamble);
@@ -541,7 +543,8 @@ void ASTWorker::update(ParseInputs Inputs, WantDiagnostics WantDiags) {
// line if there were any.
// FIXME: we might have got more errors while trying to build the AST,
// surface them too.
- Callbacks.onFailedAST(FileName, CompilerInvocationDiags, RunPublish);
+ Callbacks.onFailedAST(FileName, Inputs.Version, CompilerInvocationDiags,
+ RunPublish);
}
// Stash the AST in the cache for further use.
IdleASTs.put(this, std::move(*AST));
@@ -563,6 +566,8 @@ void ASTWorker::runWithAST(
std::unique_ptr<CompilerInvocation> Invocation = buildCompilerInvocation(
*CurrentInputs, CompilerInvocationDiagConsumer);
// Try rebuilding the AST.
+ vlog("ASTWorker rebuilding evicted AST to run {0}: {1} version {2}", Name,
+ FileName, CurrentInputs->Version);
llvm::Optional<ParsedAST> NewAST =
Invocation
? buildAST(FileName,
@@ -579,6 +584,8 @@ void ASTWorker::runWithAST(
if (!*AST)
return Action(llvm::make_error<llvm::StringError>(
"invalid AST", llvm::errc::invalid_argument));
+ vlog("ASTWorker running {0} on version {2} of {1}", Name, FileName,
+ CurrentInputs->Version);
Action(InputsAndAST{*CurrentInputs, **AST});
};
startTask(Name, std::move(Task), /*UpdateType=*/None, Invalidation);
@@ -788,8 +795,10 @@ Deadline ASTWorker::scheduleLocked() {
I->UpdateType = WantDiagnostics::Auto;
}
- while (shouldSkipHeadLocked())
+ while (shouldSkipHeadLocked()) {
+ vlog("ASTWorker skipping {0} for {1}", Requests.front().Name, FileName);
Requests.pop_front();
+ }
assert(!Requests.empty() && "skipped the whole queue");
// Some updates aren't dead yet, but never end up being used.
// e.g. the first keystroke is live until obsoleted by the second.
diff --git a/clang-tools-extra/clangd/TUScheduler.h b/clang-tools-extra/clangd/TUScheduler.h
index b2901c42bb62..5cc7e029096c 100644
--- a/clang-tools-extra/clangd/TUScheduler.h
+++ b/clang-tools-extra/clangd/TUScheduler.h
@@ -122,7 +122,8 @@ class ParsingCallbacks {
/// Called on the AST that was built for emitting the preamble. The built AST
/// contains only AST nodes from the #include directives at the start of the
/// file. AST node in the current file should be observed on onMainAST call.
- virtual void onPreambleAST(PathRef Path, ASTContext &Ctx,
+ virtual void onPreambleAST(PathRef Path, llvm::StringRef Version,
+ ASTContext &Ctx,
std::shared_ptr<clang::Preprocessor> PP,
const CanonicalIncludes &) {}
@@ -153,8 +154,8 @@ class ParsingCallbacks {
/// Called whenever the AST fails to build. \p Diags will have the diagnostics
/// that led to failure.
- virtual void onFailedAST(PathRef Path, std::vector<Diag> Diags,
- PublishFn Publish) {}
+ virtual void onFailedAST(PathRef Path, llvm::StringRef Version,
+ std::vector<Diag> Diags, PublishFn Publish) {}
/// Called whenever the TU status is updated.
virtual void onFileUpdated(PathRef File, const TUStatus &Status) {}
diff --git a/clang-tools-extra/clangd/index/FileIndex.cpp b/clang-tools-extra/clangd/index/FileIndex.cpp
index 7641aa5870ce..92e7316105c7 100644
--- a/clang-tools-extra/clangd/index/FileIndex.cpp
+++ b/clang-tools-extra/clangd/index/FileIndex.cpp
@@ -35,7 +35,7 @@ static SlabTuple indexSymbols(ASTContext &AST, std::shared_ptr<Preprocessor> PP,
llvm::ArrayRef<Decl *> DeclsToIndex,
const MainFileMacros *MacroRefsToIndex,
const CanonicalIncludes &Includes,
- bool IsIndexMainAST) {
+ bool IsIndexMainAST, llvm::StringRef Version) {
SymbolCollector::Options CollectorOpts;
CollectorOpts.CollectIncludePath = true;
CollectorOpts.Includes = &Includes;
@@ -74,12 +74,13 @@ static SlabTuple indexSymbols(ASTContext &AST, std::shared_ptr<Preprocessor> PP,
auto Refs = Collector.takeRefs();
auto Relations = Collector.takeRelations();
- vlog("index AST for {0} (main={1}): \n"
- " symbol slab: {2} symbols, {3} bytes\n"
- " ref slab: {4} symbols, {5} refs, {6} bytes\n"
- " relations slab: {7} relations, {8} bytes",
- FileName, IsIndexMainAST, Syms.size(), Syms.bytes(), Refs.size(),
- Refs.numRefs(), Refs.bytes(), Relations.size(), Relations.bytes());
+ vlog("indexed {0} AST for {1} version {2}:\n"
+ " symbol slab: {3} symbols, {4} bytes\n"
+ " ref slab: {5} symbols, {6} refs, {7} bytes\n"
+ " relations slab: {8} relations, {9} bytes",
+ IsIndexMainAST ? "file" : "preamble", FileName, Version, Syms.size(),
+ Syms.bytes(), Refs.size(), Refs.numRefs(), Refs.bytes(),
+ Relations.size(), Relations.bytes());
return std::make_tuple(std::move(Syms), std::move(Refs),
std::move(Relations));
}
@@ -88,17 +89,18 @@ SlabTuple indexMainDecls(ParsedAST &AST) {
return indexSymbols(AST.getASTContext(), AST.getPreprocessorPtr(),
AST.getLocalTopLevelDecls(), &AST.getMacros(),
AST.getCanonicalIncludes(),
- /*IsIndexMainAST=*/true);
+ /*IsIndexMainAST=*/true, AST.version());
}
-SlabTuple indexHeaderSymbols(ASTContext &AST, std::shared_ptr<Preprocessor> PP,
+SlabTuple indexHeaderSymbols(llvm::StringRef Version, ASTContext &AST,
+ std::shared_ptr<Preprocessor> PP,
const CanonicalIncludes &Includes) {
std::vector<Decl *> DeclsToIndex(
AST.getTranslationUnitDecl()->decls().begin(),
AST.getTranslationUnitDecl()->decls().end());
return indexSymbols(AST, std::move(PP), DeclsToIndex,
/*MainFileMacros=*/nullptr, Includes,
- /*IsIndexMainAST=*/false);
+ /*IsIndexMainAST=*/false, Version);
}
void FileSymbols::update(PathRef Path, std::unique_ptr<SymbolSlab> Symbols,
@@ -248,10 +250,11 @@ FileIndex::FileIndex(bool UseDex)
PreambleIndex(std::make_unique<MemIndex>()),
MainFileIndex(std::make_unique<MemIndex>()) {}
-void FileIndex::updatePreamble(PathRef Path, ASTContext &AST,
+void FileIndex::updatePreamble(PathRef Path, llvm::StringRef Version,
+ ASTContext &AST,
std::shared_ptr<Preprocessor> PP,
const CanonicalIncludes &Includes) {
- auto Slabs = indexHeaderSymbols(AST, std::move(PP), Includes);
+ auto Slabs = indexHeaderSymbols(Version, AST, std::move(PP), Includes);
PreambleSymbols.update(
Path, std::make_unique<SymbolSlab>(std::move(std::get<0>(Slabs))),
std::make_unique<RefSlab>(),
diff --git a/clang-tools-extra/clangd/index/FileIndex.h b/clang-tools-extra/clangd/index/FileIndex.h
index 50f297cc1981..ec44be9b89c2 100644
--- a/clang-tools-extra/clangd/index/FileIndex.h
+++ b/clang-tools-extra/clangd/index/FileIndex.h
@@ -98,7 +98,7 @@ class FileIndex : public MergedIndex {
/// Update preamble symbols of file \p Path with all declarations in \p AST
/// and macros in \p PP.
- void updatePreamble(PathRef Path, ASTContext &AST,
+ void updatePreamble(PathRef Path, llvm::StringRef Version, ASTContext &AST,
std::shared_ptr<Preprocessor> PP,
const CanonicalIncludes &Includes);
@@ -142,7 +142,8 @@ SlabTuple indexMainDecls(ParsedAST &AST);
/// Index declarations from \p AST and macros from \p PP that are declared in
/// included headers.
-SlabTuple indexHeaderSymbols(ASTContext &AST, std::shared_ptr<Preprocessor> PP,
+SlabTuple indexHeaderSymbols(llvm::StringRef Version, ASTContext &AST,
+ std::shared_ptr<Preprocessor> PP,
const CanonicalIncludes &Includes);
} // namespace clangd
diff --git a/clang-tools-extra/clangd/test/diagnostic-category.test b/clang-tools-extra/clangd/test/diagnostic-category.test
index 39467746c67d..654c2c0b4655 100644
--- a/clang-tools-extra/clangd/test/diagnostic-category.test
+++ b/clang-tools-extra/clangd/test/diagnostic-category.test
@@ -1,7 +1,7 @@
# RUN: clangd -lit-test < %s | FileCheck -strict-whitespace %s
{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{"textDocument":{"publishDiagnostics":{"categorySupport":true}}},"trace":"off"}}
---
-{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","version":1,"text":"struct Point {}; union Point p;"}}}
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","text":"struct Point {}; union Point p;"}}}
# CHECK: "method": "textDocument/publishDiagnostics",
# CHECK-NEXT: "params": {
# CHECK-NEXT: "diagnostics": [
@@ -37,7 +37,8 @@
# CHECK-NEXT: "severity": 3
# CHECK-NEXT: }
# CHECK-NEXT: ],
-# CHECK-NEXT: "uri": "file://{{.*}}/foo.c"
+# CHECK-NEXT: "uri": "file://{{.*}}/foo.c",
+# CHECK-NEXT: "version": 0
# CHECK-NEXT: }
---
{"jsonrpc":"2.0","id":4,"method":"shutdown"}
diff --git a/clang-tools-extra/clangd/test/diagnostics-no-tidy.test b/clang-tools-extra/clangd/test/diagnostics-no-tidy.test
index f17ab1794990..1a1068dafd5b 100644
--- a/clang-tools-extra/clangd/test/diagnostics-no-tidy.test
+++ b/clang-tools-extra/clangd/test/diagnostics-no-tidy.test
@@ -1,7 +1,7 @@
# RUN: clangd -lit-test -clang-tidy=false < %s | FileCheck -strict-whitespace %s
{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
---
-{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","version":1,"text":"void main() {\n(void)sizeof(42);\n}"}}}
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","text":"void main() {\n(void)sizeof(42);\n}"}}}
# CHECK: "method": "textDocument/publishDiagnostics",
# CHECK-NEXT: "params": {
# CHECK-NEXT: "diagnostics": [
@@ -22,7 +22,8 @@
# CHECK-NEXT: "source": "clang"
# CHECK-NEXT: }
# CHECK-NEXT: ],
-# CHECK-NEXT: "uri": "file://{{.*}}/foo.c"
+# CHECK-NEXT: "uri": "file://{{.*}}/foo.c",
+# CHECK-NEXT: "version": 0
# CHECK-NEXT: }
---
{"jsonrpc":"2.0","id":2,"method":"sync","params":null}
@@ -31,7 +32,8 @@
# CHECK: "method": "textDocument/publishDiagnostics",
# CHECK-NEXT: "params": {
# CHECK-NEXT: "diagnostics": [],
-# CHECK-NEXT: "uri": "file://{{.*}}/foo.c"
+# CHECK-NEXT: "uri": "file://{{.*}}/foo.c",
+# CHECK-NEXT: "version": null
# CHECK-NEXT: }
---
{"jsonrpc":"2.0","id":5,"method":"shutdown"}
diff --git a/clang-tools-extra/clangd/test/diagnostics-notes.test b/clang-tools-extra/clangd/test/diagnostics-notes.test
index c04c5cf0da90..9e57cf64f965 100644
--- a/clang-tools-extra/clangd/test/diagnostics-notes.test
+++ b/clang-tools-extra/clangd/test/diagnostics-notes.test
@@ -1,7 +1,7 @@
# RUN: clangd -lit-test < %s | FileCheck -strict-whitespace %s
{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{"textDocument":{"publishDiagnostics":{"relatedInformation":true}}},"trace":"off"}}
---
-{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.cc","languageId":"cpp","version":1,"text":"int x;\nint x;"}}}
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.cc","languageId":"cpp","text":"int x;\nint x;"}}}
# CHECK: "method": "textDocument/publishDiagnostics",
# CHECK-NEXT: "params": {
# CHECK-NEXT: "diagnostics": [
@@ -40,7 +40,8 @@
# CHECK-NEXT: "source": "clang"
# CHECK-NEXT: }
# CHECK-NEXT: ],
-# CHECK-NEXT: "uri": "file://{{.*}}/foo.cc"
+# CHECK-NEXT: "uri": "file://{{.*}}/foo.cc",
+# CHECK-NEXT: "version": 0
# CHECK-NEXT: }
---
{"jsonrpc":"2.0","id":5,"method":"shutdown"}
diff --git a/clang-tools-extra/clangd/test/diagnostics.test b/clang-tools-extra/clangd/test/diagnostics.test
index accfd17e0567..6f54e2cf115a 100644
--- a/clang-tools-extra/clangd/test/diagnostics.test
+++ b/clang-tools-extra/clangd/test/diagnostics.test
@@ -1,7 +1,7 @@
# RUN: clangd -lit-test -clang-tidy-checks=bugprone-sizeof-expression < %s | FileCheck -strict-whitespace %s
{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
---
-{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","version":1,"text":"void main() {\n(void)sizeof(42);\n}"}}}
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","text":"void main() {\n(void)sizeof(42);\n}"}}}
# CHECK: "method": "textDocument/publishDiagnostics",
# CHECK-NEXT: "params": {
# CHECK-NEXT: "diagnostics": [
@@ -38,7 +38,8 @@
# CHECK-NEXT: "source": "clang-tidy"
# CHECK-NEXT: }
# CHECK-NEXT: ],
-# CHECK-NEXT: "uri": "file://{{.*}}/foo.c"
+# CHECK-NEXT: "uri": "file://{{.*}}/foo.c",
+# CHECK-NEXT: "version": 0
# CHECK-NEXT: }
---
{"jsonrpc":"2.0","id":2,"method":"sync","params":null}
@@ -47,7 +48,8 @@
# CHECK: "method": "textDocument/publishDiagnostics",
# CHECK-NEXT: "params": {
# CHECK-NEXT: "diagnostics": [],
-# CHECK-NEXT: "uri": "file://{{.*}}/foo.c"
+# CHECK-NEXT: "uri": "file://{{.*}}/foo.c",
+# CHECK-NEXT: "version": null
# CHECK-NEXT: }
---
{"jsonrpc":"2.0","id":5,"method":"shutdown"}
diff --git a/clang-tools-extra/clangd/test/did-change-configuration-params.test b/clang-tools-extra/clangd/test/did-change-configuration-params.test
index 6f9b537e5414..4aef1011b370 100644
--- a/clang-tools-extra/clangd/test/did-change-configuration-params.test
+++ b/clang-tools-extra/clangd/test/did-change-configuration-params.test
@@ -5,18 +5,20 @@
---
{"jsonrpc":"2.0","method":"workspace/didChangeConfiguration","params":{"settings":{"compilationDatabaseChanges":{"/clangd-test/foo.c": {"workingDirectory":"/clangd-test", "compilationCommand": ["clang", "-c", "foo.c"]}}}}}
---
-{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","version":1,"text":"int main() { int i; return i; }"}}}
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","text":"int main() { int i; return i; }"}}}
# CHECK: "method": "textDocument/publishDiagnostics",
# CHECK-NEXT: "params": {
# CHECK-NEXT: "diagnostics": [],
-# CHECK-NEXT: "uri": "file://{{.*}}/foo.c"
+# CHECK-NEXT: "uri": "file://{{.*}}/foo.c",
+# CHECK-NEXT: "version": 0
# CHECK-NEXT: }
---
-{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///bar.c","languageId":"c","version":1,"text":"int main() { int i; return i; }"}}}
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///bar.c","languageId":"c","text":"int main() { int i; return i; }"}}}
# CHECK: "method": "textDocument/publishDiagnostics",
# CHECK-NEXT: "params": {
# CHECK-NEXT: "diagnostics": [],
-# CHECK-NEXT: "uri": "file://{{.*}}/bar.c"
+# CHECK-NEXT: "uri": "file://{{.*}}/bar.c",
+# CHECK-NEXT: "version": 0
# CHECK-NEXT: }
---
{"jsonrpc":"2.0","method":"workspace/didChangeConfiguration","params":{"settings":{"compilationDatabaseChanges":{"/clangd-test/foo.c": {"workingDirectory":"/clangd-test2", "compilationCommand": ["clang", "-c", "foo.c", "-Wall", "-Werror"]}}}}}
@@ -40,10 +42,11 @@
# CHECK-NEXT: "source": "clang"
# CHECK-NEXT: }
# CHECK-NEXT: ],
-# CHECK-NEXT: "uri": "file://{{.*}}/foo.c"
+# CHECK-NEXT: "uri": "file://{{.*}}/foo.c",
+# CHECK-NEXT: "version": 0
# CHECK-NEXT: }
#
-# ERR: Updating file {{.*}}foo.c with command
+# ERR: ASTWorker building file {{.*}}foo.c version 0 with command
# ERR: [{{.*}}clangd-test2]
# ERR: clang -c foo.c -Wall -Werror
---
diff --git a/clang-tools-extra/clangd/test/execute-command.test b/clang-tools-extra/clangd/test/execute-command.test
index 7abd79e54cb2..b4bd48b784f2 100644
--- a/clang-tools-extra/clangd/test/execute-command.test
+++ b/clang-tools-extra/clangd/test/execute-command.test
@@ -1,7 +1,7 @@
# RUN: clangd -lit-test < %s | FileCheck -strict-whitespace %s
{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
---
-{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","version":1,"text":"int main(int i, char **a) { if (i = 2) {}}"}}}
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","text":"int main(int i, char **a) { if (i = 2) {}}"}}}
# CHECK: "method": "textDocument/publishDiagnostics",
# CHECK-NEXT: "params": {
# CHECK-NEXT: "diagnostics": [
@@ -22,7 +22,8 @@
# CHECK-NEXT: "source": "clang"
# CHECK-NEXT: }
# CHECK-NEXT: ],
-# CHECK-NEXT: "uri": "file://{{.*}}/foo.c"
+# CHECK-NEXT: "uri": "file://{{.*}}/foo.c",
+# CHECK-NEXT: "version": 0
# CHECK-NEXT: }
---
# No command name
diff --git a/clang-tools-extra/clangd/test/fixits-codeaction.test b/clang-tools-extra/clangd/test/fixits-codeaction.test
index e9190711795a..75d0fb012ffc 100644
--- a/clang-tools-extra/clangd/test/fixits-codeaction.test
+++ b/clang-tools-extra/clangd/test/fixits-codeaction.test
@@ -1,7 +1,7 @@
# RUN: clangd -lit-test < %s | FileCheck -strict-whitespace %s
{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{"textDocument":{"codeAction":{"codeActionLiteralSupport":{}}}},"trace":"off"}}
---
-{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","version":1,"text":"int main(int i, char **a) { if (i = 2) {}}"}}}
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","text":"int main(int i, char **a) { if (i = 2) {}}"}}}
# CHECK: "method": "textDocument/publishDiagnostics",
# CHECK-NEXT: "params": {
# CHECK-NEXT: "diagnostics": [
@@ -22,7 +22,8 @@
# CHECK-NEXT: "source": "clang"
# CHECK-NEXT: }
# CHECK-NEXT: ],
-# CHECK-NEXT: "uri": "file://{{.*}}/foo.c"
+# CHECK-NEXT: "uri": "file://{{.*}}/foo.c",
+# CHECK-NEXT: "version": 0
# CHECK-NEXT: }
---
{"jsonrpc":"2.0","id":2,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"test:///foo.c"},"range":{"start":{"line":0,"character":13},"end":{"line":0,"character":35}},"context":{"diagnostics":[{"range":{"start": {"line": 0, "character": 32}, "end": {"line": 0, "character": 37}},"severity":2,"message":"Using the result of an assignment as a condition without parentheses (fixes available)", "code": "-Wparentheses", "source": "clang"}]}}}
diff --git a/clang-tools-extra/clangd/test/fixits-command.test b/clang-tools-extra/clangd/test/fixits-command.test
index 6cc7f01c4e8b..62b5a6152d2c 100644
--- a/clang-tools-extra/clangd/test/fixits-command.test
+++ b/clang-tools-extra/clangd/test/fixits-command.test
@@ -1,7 +1,7 @@
# RUN: clangd -lit-test < %s | FileCheck -strict-whitespace %s
{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
---
-{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","version":1,"text":"int main(int i, char **a) { if (i = 2) {}}"}}}
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","text":"int main(int i, char **a) { if (i = 2) {}}"}}}
# CHECK: "method": "textDocument/publishDiagnostics",
# CHECK-NEXT: "params": {
# CHECK-NEXT: "diagnostics": [
@@ -22,7 +22,8 @@
# CHECK-NEXT: "source": "clang"
# CHECK-NEXT: }
# CHECK-NEXT: ],
-# CHECK-NEXT: "uri": "file://{{.*}}/foo.c"
+# CHECK-NEXT: "uri": "file://{{.*}}/foo.c",
+# CHECK-NEXT: "version": 0
# CHECK-NEXT: }
---
{"jsonrpc":"2.0","id":2,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"test:///foo.c"},"range":{"start":{"line":0,"character":13},"end":{"line":0,"character":35}},"context":{"diagnostics":[{"range":{"start": {"line": 0, "character": 32}, "end": {"line": 0, "character": 37}},"severity":2,"message":"Using the result of an assignment as a condition without parentheses (fixes available)"}]}}}
diff --git a/clang-tools-extra/clangd/test/fixits-embed-in-diagnostic.test b/clang-tools-extra/clangd/test/fixits-embed-in-diagnostic.test
index 7d2cccdb4337..cfa47210d7d8 100644
--- a/clang-tools-extra/clangd/test/fixits-embed-in-diagnostic.test
+++ b/clang-tools-extra/clangd/test/fixits-embed-in-diagnostic.test
@@ -1,7 +1,7 @@
# RUN: clangd -lit-test < %s | FileCheck -strict-whitespace %s
{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{"textDocument":{"publishDiagnostics":{"codeActionsInline":true}}},"trace":"off"}}
---
-{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","version":1,"text":"struct Point {}; union Point p;"}}}
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","text":"struct Point {}; union Point p;"}}}
# CHECK: "method": "textDocument/publishDiagnostics",
# CHECK-NEXT: "params": {
# CHECK-NEXT: "diagnostics": [
@@ -61,7 +61,8 @@
# CHECK-NEXT: "severity": 3
# CHECK-NEXT: }
# CHECK-NEXT: ],
-# CHECK-NEXT: "uri": "file://{{.*}}/foo.c"
+# CHECK-NEXT: "uri": "file://{{.*}}/foo.c",
+# CHECK-NEXT: "version": 0
# CHECK-NEXT: }
---
{"jsonrpc":"2.0","id":4,"method":"shutdown"}
diff --git a/clang-tools-extra/clangd/test/path-mappings.test b/clang-tools-extra/clangd/test/path-mappings.test
index b0e54a61e067..c566905c22eb 100644
--- a/clang-tools-extra/clangd/test/path-mappings.test
+++ b/clang-tools-extra/clangd/test/path-mappings.test
@@ -12,7 +12,6 @@
"textDocument": {
"uri": "file:///C:/client/bar.cpp",
"languageId": "cpp",
- "version": 1,
"text": "#include \"foo.h\"\nint main(){\nreturn foo();\n}"
}
}
@@ -21,7 +20,8 @@
# CHECK: "method": "textDocument/publishDiagnostics",
# CHECK-NEXT: "params": {
# CHECK-NEXT: "diagnostics": [],
-# CHECK-NEXT: "uri": "file:///C:/client/bar.cpp"
+# CHECK-NEXT: "uri": "file:///C:/client/bar.cpp",
+# CHECK-NEXT: "version": 0
# CHECK-NEXT: }
---
# We're editing bar.cpp, which includes foo.h, where foo.h "exists" at a server location
@@ -47,7 +47,7 @@
# CHECK-NEXT: "range": {
# CHECK-NEXT: "end": {
# CHECK-NEXT: "character": {{[0-9]+}},
-# CHECK-NEXT: "line": {{[0-9]+}}
+# CHECK-NEXT: "line": {{[0-9]+}}
# CHECK-NEXT: },
# CHECK-NEXT: "start": {
# CHECK-NEXT: "character": {{[0-9]+}},
diff --git a/clang-tools-extra/clangd/test/semantic-highlighting.test b/clang-tools-extra/clangd/test/semantic-highlighting.test
index fca2b09a14eb..bca6b373aa22 100644
--- a/clang-tools-extra/clangd/test/semantic-highlighting.test
+++ b/clang-tools-extra/clangd/test/semantic-highlighting.test
@@ -67,7 +67,7 @@
# CHECK-NEXT: ]
# CHECK-NEXT: },
---
-{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.cpp","languageId":"cpp","version":1,"text":"int x = 2;"}}}
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.cpp","languageId":"cpp","text":"int x = 2;"}}}
# CHECK: "method": "textDocument/semanticHighlighting",
# CHECK-NEXT: "params": {
# CHECK-NEXT: "lines": [
@@ -78,12 +78,13 @@
# CHECK-NEXT: }
# CHECK-NEXT: ],
# CHECK-NEXT: "textDocument": {
-# CHECK-NEXT: "uri": "file://{{.*}}/clangd-test/foo.cpp"
+# CHECK-NEXT: "uri": "file://{{.*}}/clangd-test/foo.cpp",
+# CHECK-NEXT: "version": 0
# CHECK-NEXT: }
# CHECK-NEXT: }
# CHECK-NEXT:}
---
-{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo2.cpp","languageId":"cpp","version":1,"text":"int x = 2;\nint y = 2;"}}}
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo2.cpp","languageId":"cpp","text":"int x = 2;\nint y = 2;"}}}
# CHECK: "method": "textDocument/semanticHighlighting",
# CHECK-NEXT: "params": {
# CHECK-NEXT: "lines": [
@@ -99,12 +100,13 @@
# CHECK-NEXT: }
# CHECK-NEXT: ],
# CHECK-NEXT: "textDocument": {
-# CHECK-NEXT: "uri": "file://{{.*}}/clangd-test/foo2.cpp"
+# CHECK-NEXT: "uri": "file://{{.*}}/clangd-test/foo2.cpp",
+# CHECK-NEXT: "version": 0
# CHECK-NEXT: }
# CHECK-NEXT: }
# CHECK-NEXT:}
---
-{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"test:///foo.cpp","version":2},"contentChanges": [{"range":{"start": {"line": 0,"character": 10},"end": {"line": 0,"character": 10}},"rangeLength": 0,"text": "\nint y = 2;"}]}}
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"test:///foo.cpp"},"contentChanges": [{"range":{"start": {"line": 0,"character": 10},"end": {"line": 0,"character": 10}},"rangeLength": 0,"text": "\nint y = 2;"}]}}
# CHECK: "method": "textDocument/semanticHighlighting",
# CHECK-NEXT: "params": {
# CHECK-NEXT: "lines": [
@@ -115,12 +117,13 @@
# CHECK-NEXT: }
# CHECK-NEXT: ],
# CHECK-NEXT: "textDocument": {
-# CHECK-NEXT: "uri": "file://{{.*}}/clangd-test/foo.cpp"
+# CHECK-NEXT: "uri": "file://{{.*}}/clangd-test/foo.cpp",
+# CHECK-NEXT: "version": 1
# CHECK-NEXT: }
# CHECK-NEXT: }
# CHECK-NEXT:}
---
-{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"test:///foo.cpp","version":2},"contentChanges": [{"range":{"start": {"line": 0,"character": 10},"end": {"line": 1,"character": 10}},"rangeLength": 11,"text": ""}]}}
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"test:///foo.cpp"},"contentChanges": [{"range":{"start": {"line": 0,"character": 10},"end": {"line": 1,"character": 10}},"rangeLength": 11,"text": ""}]}}
# CHECK: "method": "textDocument/semanticHighlighting",
# CHECK-NEXT: "params": {
# CHECK-NEXT: "lines": [
@@ -131,7 +134,8 @@
# CHECK-NEXT: }
# CHECK-NEXT: ],
# CHECK-NEXT: "textDocument": {
-# CHECK-NEXT: "uri": "file://{{.*}}/clangd-test/foo.cpp"
+# CHECK-NEXT: "uri": "file://{{.*}}/clangd-test/foo.cpp",
+# CHECK-NEXT: "version": 2
# CHECK-NEXT: }
# CHECK-NEXT: }
# CHECK-NEXT:}
diff --git a/clang-tools-extra/clangd/test/version.test b/clang-tools-extra/clangd/test/version.test
new file mode 100644
index 000000000000..fa2c83bac477
--- /dev/null
+++ b/clang-tools-extra/clangd/test/version.test
@@ -0,0 +1,25 @@
+# RUN: clangd -lit-test < %s | FileCheck -strict-whitespace %s
+# Verify versions get recorded/inferred, and are reported in publishDiagnostics.
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{}}
+---
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","text":""}}}
+# CHECK: "version": 0
+---
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"test:///foo.c","version":5},"contentChanges":[{"text":"a"}]}}
+# CHECK: "version": 5
+---
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"test:///foo.c"},"contentChanges":[{"text":"b"}]}}
+# CHECK: "version": 6
+---
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///bar.c","version": 42, "languageId":"c","text":""}}}
+# CHECK: "version": 42
+---
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"test:///bar.c"},"contentChanges":[{"text":"c"}]}}
+# CHECK: "version": 43
+---
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"test:///bar.c", "version": 123},"contentChanges":[{"text":"d"}]}}
+# CHECK: "version": 123
+---
+{"jsonrpc":"2.0","id":6,"method":"shutdown"}
+---
+{"jsonrpc":"2.0","method":"exit"}
diff --git a/clang-tools-extra/clangd/unittests/ClangdTests.cpp b/clang-tools-extra/clangd/unittests/ClangdTests.cpp
index d714fac4717c..7ac75b8b4e29 100644
--- a/clang-tools-extra/clangd/unittests/ClangdTests.cpp
+++ b/clang-tools-extra/clangd/unittests/ClangdTests.cpp
@@ -61,7 +61,7 @@ bool diagsContainErrors(const std::vector<Diag> &Diagnostics) {
class ErrorCheckingCallbacks : public ClangdServer::Callbacks {
public:
- void onDiagnosticsReady(PathRef File,
+ void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
std::vector<Diag> Diagnostics) override {
bool HadError = diagsContainErrors(Diagnostics);
std::lock_guard<std::mutex> Lock(Mutex);
@@ -82,7 +82,7 @@ class ErrorCheckingCallbacks : public ClangdServer::Callbacks {
/// least one error.
class MultipleErrorCheckingCallbacks : public ClangdServer::Callbacks {
public:
- void onDiagnosticsReady(PathRef File,
+ void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
std::vector<Diag> Diagnostics) override {
bool HadError = diagsContainErrors(Diagnostics);
@@ -276,7 +276,7 @@ TEST_F(ClangdVFSTest, PropagatesContexts) {
mutable int Got;
} FS;
struct Callbacks : public ClangdServer::Callbacks {
- void onDiagnosticsReady(PathRef File,
+ void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
std::vector<Diag> Diagnostics) override {
Got = Context::current().getExisting(Secret);
}
@@ -295,6 +295,23 @@ TEST_F(ClangdVFSTest, PropagatesContexts) {
EXPECT_EQ(Callbacks.Got, 42);
}
+TEST_F(ClangdVFSTest, PropagatesVersion) {
+ MockCompilationDatabase CDB;
+ MockFSProvider FS;
+ struct Callbacks : public ClangdServer::Callbacks {
+ void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
+ std::vector<Diag> Diagnostics) override {
+ Got = Version.str();
+ }
+ std::string Got = "";
+ } Callbacks;
+
+ // Verify that the version is plumbed to diagnostics.
+ ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &Callbacks);
+ runAddDocument(Server, testPath("foo.cpp"), "void main(){}", "42");
+ EXPECT_EQ(Callbacks.Got, "42");
+}
+
// Only enable this test on Unix
#ifdef LLVM_ON_UNIX
TEST_F(ClangdVFSTest, SearchLibDir) {
@@ -374,7 +391,7 @@ struct bar { T x; };
// Now switch to C++ mode.
CDB.ExtraClangFlags = {"-xc++"};
- runAddDocument(Server, FooCpp, SourceContents2, WantDiagnostics::Auto);
+ runAddDocument(Server, FooCpp, SourceContents2);
EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
// Subsequent addDocument calls should finish without errors too.
runAddDocument(Server, FooCpp, SourceContents1);
@@ -406,7 +423,7 @@ int main() { return 0; }
// Parse without the define, no errors should be produced.
CDB.ExtraClangFlags = {};
- runAddDocument(Server, FooCpp, SourceContents, WantDiagnostics::Auto);
+ runAddDocument(Server, FooCpp, SourceContents);
ASSERT_TRUE(Server.blockUntilIdleForTest());
EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
// Subsequent addDocument call should finish without errors too.
@@ -467,8 +484,8 @@ int hello;
CDB.ExtraClangFlags.clear();
DiagConsumer.clear();
Server.removeDocument(BazCpp);
- Server.addDocument(FooCpp, FooSource.code(), WantDiagnostics::Auto);
- Server.addDocument(BarCpp, BarSource.code(), WantDiagnostics::Auto);
+ Server.addDocument(FooCpp, FooSource.code());
+ Server.addDocument(BarCpp, BarSource.code());
ASSERT_TRUE(Server.blockUntilIdleForTest());
EXPECT_THAT(DiagConsumer.filesWithDiags(),
@@ -595,7 +612,7 @@ int d;
public:
TestDiagConsumer() : Stats(FilesCount, FileStat()) {}
- void onDiagnosticsReady(PathRef File,
+ void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
std::vector<Diag> Diagnostics) override {
StringRef FileIndexStr = llvm::sys::path::stem(File);
ASSERT_TRUE(FileIndexStr.consume_front("Foo"));
@@ -672,8 +689,7 @@ int d;
bool ShouldHaveErrors = ShouldHaveErrorsDist(RandGen);
Server.addDocument(FilePaths[FileIndex],
ShouldHaveErrors ? SourceContentsWithErrors
- : SourceContentsWithoutErrors,
- WantDiagnostics::Auto);
+ : SourceContentsWithoutErrors);
UpdateStatsOnAddDocument(FileIndex, ShouldHaveErrors);
};
@@ -775,7 +791,8 @@ TEST_F(ClangdThreadingTest, NoConcurrentDiagnostics) {
NoConcurrentAccessDiagConsumer(std::promise<void> StartSecondReparse)
: StartSecondReparse(std::move(StartSecondReparse)) {}
- void onDiagnosticsReady(PathRef, std::vector<Diag>) override {
+ void onDiagnosticsReady(PathRef, llvm::StringRef,
+ std::vector<Diag>) override {
++Count;
std::unique_lock<std::mutex> Lock(Mutex, std::try_to_lock_t());
ASSERT_TRUE(Lock.owns_lock())
diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
index f9ffe1167338..e61dca9cf4fb 100644
--- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
+++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
@@ -1509,7 +1509,7 @@ TEST(CompletionTest, DocumentationFromChangedFileCrash) {
}
int a = fun^
)cpp");
- Server.addDocument(FooCpp, Source.code(), WantDiagnostics::Yes);
+ Server.addDocument(FooCpp, Source.code(), "null", WantDiagnostics::Yes);
// We need to wait for preamble to build.
ASSERT_TRUE(Server.blockUntilIdleForTest());
@@ -1575,7 +1575,7 @@ TEST(CompletionTest, NonDocComments) {
// FIXME: Auto-completion in a template requires disabling delayed template
// parsing.
CDB.ExtraClangFlags.push_back("-fno-delayed-template-parsing");
- runAddDocument(Server, FooCpp, Source.code(), WantDiagnostics::Yes);
+ runAddDocument(Server, FooCpp, Source.code(), "null", WantDiagnostics::Yes);
CodeCompleteResult Completions = cantFail(runCodeComplete(
Server, FooCpp, Source.point(), clangd::CodeCompleteOptions()));
diff --git a/clang-tools-extra/clangd/unittests/FileIndexTests.cpp b/clang-tools-extra/clangd/unittests/FileIndexTests.cpp
index e472dd9b165d..7c9aa3842531 100644
--- a/clang-tools-extra/clangd/unittests/FileIndexTests.cpp
+++ b/clang-tools-extra/clangd/unittests/FileIndexTests.cpp
@@ -151,8 +151,8 @@ void update(FileIndex &M, llvm::StringRef Basename, llvm::StringRef Code) {
File.HeaderFilename = (Basename + ".h").str();
File.HeaderCode = std::string(Code);
auto AST = File.build();
- M.updatePreamble(File.Filename, AST.getASTContext(), AST.getPreprocessorPtr(),
- AST.getCanonicalIncludes());
+ M.updatePreamble(File.Filename, /*Version=*/"null", AST.getASTContext(),
+ AST.getPreprocessorPtr(), AST.getCanonicalIncludes());
}
TEST(FileIndexTest, CustomizedURIScheme) {
@@ -293,7 +293,8 @@ TEST(FileIndexTest, RebuildWithPreamble) {
const CanonicalIncludes &CanonIncludes) {
EXPECT_FALSE(IndexUpdated) << "Expected only a single index update";
IndexUpdated = true;
- Index.updatePreamble(FooCpp, Ctx, std::move(PP), CanonIncludes);
+ Index.updatePreamble(FooCpp, /*Version=*/"null", Ctx, std::move(PP),
+ CanonIncludes);
});
ASSERT_TRUE(IndexUpdated);
@@ -392,7 +393,7 @@ TEST(FileIndexTest, Relations) {
TU.HeaderCode = "class A {}; class B : public A {};";
auto AST = TU.build();
FileIndex Index;
- Index.updatePreamble(TU.Filename, AST.getASTContext(),
+ Index.updatePreamble(TU.Filename, /*Version=*/"null", AST.getASTContext(),
AST.getPreprocessorPtr(), AST.getCanonicalIncludes());
SymbolID A = findSymbol(TU.headerSymbols(), "A").ID;
uint32_t Results = 0;
diff --git a/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp b/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp
index f643f6c22adb..8672a043f186 100644
--- a/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp
@@ -702,7 +702,8 @@ TEST(SemanticHighlighting, GeneratesHighlightsWhenFileChange) {
std::atomic<int> Count = {0};
void onHighlightingsReady(
- PathRef File, std::vector<HighlightingToken> Highlightings) override {
+ PathRef File, llvm::StringRef Version,
+ std::vector<HighlightingToken> Highlightings) override {
++Count;
}
};
diff --git a/clang-tools-extra/clangd/unittests/SyncAPI.cpp b/clang-tools-extra/clangd/unittests/SyncAPI.cpp
index e971cd6d51e6..522aaed117e8 100644
--- a/clang-tools-extra/clangd/unittests/SyncAPI.cpp
+++ b/clang-tools-extra/clangd/unittests/SyncAPI.cpp
@@ -13,8 +13,9 @@ namespace clang {
namespace clangd {
void runAddDocument(ClangdServer &Server, PathRef File,
- llvm::StringRef Contents, WantDiagnostics WantDiags) {
- Server.addDocument(File, Contents, WantDiags);
+ llvm::StringRef Contents, llvm::StringRef Version,
+ WantDiagnostics WantDiags, bool ForceRebuild) {
+ Server.addDocument(File, Contents, Version, WantDiags, ForceRebuild);
if (!Server.blockUntilIdleForTest())
llvm_unreachable("not idle after addDocument");
}
diff --git a/clang-tools-extra/clangd/unittests/SyncAPI.h b/clang-tools-extra/clangd/unittests/SyncAPI.h
index fc3c3f4b6c2a..241b98d34894 100644
--- a/clang-tools-extra/clangd/unittests/SyncAPI.h
+++ b/clang-tools-extra/clangd/unittests/SyncAPI.h
@@ -23,7 +23,9 @@ namespace clangd {
// Calls addDocument and then blockUntilIdleForTest.
void runAddDocument(ClangdServer &Server, PathRef File, StringRef Contents,
- WantDiagnostics WantDiags = WantDiagnostics::Auto);
+ StringRef Version = "null",
+ WantDiagnostics WantDiags = WantDiagnostics::Auto,
+ bool ForceRebuild = false);
llvm::Expected<CodeCompleteResult>
runCodeComplete(ClangdServer &Server, PathRef File, Position Pos,
diff --git a/clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp b/clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp
index 365e534fd66c..6ca6e5479677 100644
--- a/clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp
+++ b/clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp
@@ -41,7 +41,15 @@ using ::testing::Pointee;
using ::testing::UnorderedElementsAre;
MATCHER_P2(TUState, State, ActionName, "") {
- return arg.Action.S == State && arg.Action.Name == ActionName;
+ if (arg.Action.S != State) {
+ *result_listener << "state is " << arg.Action.S;
+ return false;
+ }
+ if (arg.Action.Name != ActionName) {
+ *result_listener << "name is " << arg.Action.Name;
+ return false;
+ }
+ return true;
}
TUScheduler::Options optsForTest() {
@@ -62,8 +70,15 @@ class TUSchedulerTests : public ::testing::Test {
void updateWithCallback(TUScheduler &S, PathRef File,
llvm::StringRef Contents, WantDiagnostics WD,
llvm::unique_function<void()> CB) {
+ updateWithCallback(S, File, getInputs(File, std::string(Contents)), WD,
+ std::move(CB));
+ }
+
+ void updateWithCallback(TUScheduler &S, PathRef File, ParseInputs Inputs,
+ WantDiagnostics WD,
+ llvm::unique_function<void()> CB) {
WithContextValue Ctx(llvm::make_scope_exit(std::move(CB)));
- S.update(File, getInputs(File, std::string(Contents)), WD);
+ S.update(File, Inputs, WD);
}
static Key<llvm::unique_function<void(PathRef File, std::vector<Diag>)>>
@@ -78,8 +93,8 @@ class TUSchedulerTests : public ::testing::Test {
reportDiagnostics(File, AST.getDiagnostics(), Publish);
}
- void onFailedAST(PathRef File, std::vector<Diag> Diags,
- PublishFn Publish) override {
+ void onFailedAST(PathRef File, llvm::StringRef Version,
+ std::vector<Diag> Diags, PublishFn Publish) override {
reportDiagnostics(File, Diags, Publish);
}
@@ -244,7 +259,9 @@ TEST_F(TUSchedulerTests, PreambleConsistency) {
// Schedule two updates (A, B) and two preamble reads (stale, consistent).
// The stale read should see A, and the consistent read should see B.
// (We recognize the preambles by their included files).
- updateWithCallback(S, Path, "#include <A>", WantDiagnostics::Yes, [&]() {
+ auto Inputs = getInputs(Path, "#include <A>");
+ Inputs.Version = "A";
+ updateWithCallback(S, Path, Inputs, WantDiagnostics::Yes, [&]() {
// This callback runs in between the two preamble updates.
// This blocks update B, preventing it from winning the race
@@ -257,12 +274,14 @@ TEST_F(TUSchedulerTests, PreambleConsistency) {
// If the second read was stale, it would usually see A.
std::this_thread::sleep_for(std::chrono::milliseconds(100));
});
- S.update(Path, getInputs(Path, "#include <B>"), WantDiagnostics::Yes);
+ Inputs.Contents = "#include <B>";
+ Inputs.Version = "B";
+ S.update(Path, Inputs, WantDiagnostics::Yes);
S.runWithPreamble("StaleRead", Path, TUScheduler::Stale,
[&](Expected<InputsAndPreamble> Pre) {
ASSERT_TRUE(bool(Pre));
- assert(bool(Pre));
+ EXPECT_EQ(Pre->Preamble->Version, "A");
EXPECT_THAT(includes(Pre->Preamble),
ElementsAre("<A>"));
InconsistentReadDone.notify();
@@ -271,6 +290,7 @@ TEST_F(TUSchedulerTests, PreambleConsistency) {
S.runWithPreamble("ConsistentRead", Path, TUScheduler::Consistent,
[&](Expected<InputsAndPreamble> Pre) {
ASSERT_TRUE(bool(Pre));
+ EXPECT_EQ(Pre->Preamble->Version, "B");
EXPECT_THAT(includes(Pre->Preamble),
ElementsAre("<B>"));
++CallbackCount;
@@ -446,6 +466,7 @@ TEST_F(TUSchedulerTests, ManyUpdates) {
auto Inputs = getInputs(File, Contents.str());
{
WithContextValue WithNonce(NonceKey, ++Nonce);
+ Inputs.Version = Nonce;
updateWithDiags(
S, File, Inputs, WantDiagnostics::Auto,
[File, Nonce, &Mut, &TotalUpdates](std::vector<Diag>) {
@@ -467,6 +488,8 @@ TEST_F(TUSchedulerTests, ManyUpdates) {
ASSERT_TRUE((bool)AST);
EXPECT_EQ(AST->Inputs.FS, Inputs.FS);
EXPECT_EQ(AST->Inputs.Contents, Inputs.Contents);
+ EXPECT_EQ(AST->Inputs.Version, Inputs.Version);
+ EXPECT_EQ(AST->AST.version(), Inputs.Version);
std::lock_guard<std::mutex> Lock(Mut);
++TotalASTReads;
@@ -769,9 +792,6 @@ TEST_F(TUSchedulerTests, Run) {
TEST_F(TUSchedulerTests, TUStatus) {
class CaptureTUStatus : public ClangdServer::Callbacks {
public:
- void onDiagnosticsReady(PathRef File,
- std::vector<Diag> Diagnostics) override {}
-
void onFileUpdated(PathRef File, const TUStatus &Status) override {
std::lock_guard<std::mutex> Lock(Mutex);
AllStatus.push_back(Status);
@@ -793,7 +813,8 @@ TEST_F(TUSchedulerTests, TUStatus) {
// We schedule the following tasks in the queue:
// [Update] [GoToDefinition]
- Server.addDocument(testPath("foo.cpp"), Code.code(), WantDiagnostics::Yes);
+ Server.addDocument(testPath("foo.cpp"), Code.code(), "1",
+ WantDiagnostics::Yes);
Server.locateSymbolAt(testPath("foo.cpp"), Code.point(),
[](Expected<std::vector<LocatedSymbol>> Result) {
ASSERT_TRUE((bool)Result);
@@ -804,9 +825,9 @@ TEST_F(TUSchedulerTests, TUStatus) {
EXPECT_THAT(CaptureTUStatus.allStatus(),
ElementsAre(
// Statuses of "Update" action.
- TUState(TUAction::RunningAction, "Update"),
- TUState(TUAction::BuildingPreamble, "Update"),
- TUState(TUAction::BuildingFile, "Update"),
+ TUState(TUAction::RunningAction, "Update (1)"),
+ TUState(TUAction::BuildingPreamble, "Update (1)"),
+ TUState(TUAction::BuildingFile, "Update (1)"),
// Statuses of "Definitions" action
TUState(TUAction::RunningAction, "Definitions"),
diff --git a/clang-tools-extra/clangd/unittests/TestTU.cpp b/clang-tools-extra/clangd/unittests/TestTU.cpp
index 54717338b01c..18f0589f5d17 100644
--- a/clang-tools-extra/clangd/unittests/TestTU.cpp
+++ b/clang-tools-extra/clangd/unittests/TestTU.cpp
@@ -97,7 +97,7 @@ ParsedAST TestTU::build() const {
SymbolSlab TestTU::headerSymbols() const {
auto AST = build();
- return std::get<0>(indexHeaderSymbols(AST.getASTContext(),
+ return std::get<0>(indexHeaderSymbols(/*Version=*/"null", AST.getASTContext(),
AST.getPreprocessorPtr(),
AST.getCanonicalIncludes()));
}
@@ -105,8 +105,8 @@ SymbolSlab TestTU::headerSymbols() const {
std::unique_ptr<SymbolIndex> TestTU::index() const {
auto AST = build();
auto Idx = std::make_unique<FileIndex>(/*UseDex=*/true);
- Idx->updatePreamble(Filename, AST.getASTContext(), AST.getPreprocessorPtr(),
- AST.getCanonicalIncludes());
+ Idx->updatePreamble(Filename, /*Version=*/"null", AST.getASTContext(),
+ AST.getPreprocessorPtr(), AST.getCanonicalIncludes());
Idx->updateMain(Filename, AST);
return std::move(Idx);
}
diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp b/clang-tools-extra/clangd/unittests/XRefsTests.cpp
index ef0416ba91de..098a9421a8bb 100644
--- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp
+++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp
@@ -849,7 +849,8 @@ TEST(LocateSymbol, WithPreamble) {
ElementsAre(Sym("foo.h", FooHeader.range())));
// Only preamble is built, and no AST is built in this request.
- Server.addDocument(FooCpp, FooWithoutHeader.code(), WantDiagnostics::No);
+ Server.addDocument(FooCpp, FooWithoutHeader.code(), "null",
+ WantDiagnostics::No);
// We build AST here, and it should use the latest preamble rather than the
// stale one.
EXPECT_THAT(
@@ -859,7 +860,8 @@ TEST(LocateSymbol, WithPreamble) {
// Reset test environment.
runAddDocument(Server, FooCpp, FooWithHeader.code());
// Both preamble and AST are built in this request.
- Server.addDocument(FooCpp, FooWithoutHeader.code(), WantDiagnostics::Yes);
+ Server.addDocument(FooCpp, FooWithoutHeader.code(), "null",
+ WantDiagnostics::Yes);
// Use the AST being built in above request.
EXPECT_THAT(
cantFail(runLocateSymbolAt(Server, FooCpp, FooWithoutHeader.point())),
More information about the cfe-commits
mailing list