diff --git a/clang-tools-extra/clangd/ClangdServer.h b/clang-tools-extra/clangd/ClangdServer.h --- a/clang-tools-extra/clangd/ClangdServer.h +++ b/clang-tools-extra/clangd/ClangdServer.h @@ -317,6 +317,13 @@ void semanticHighlights(PathRef File, Callback>); + /// Runs an arbitrary action that has access to the AST of the specified file. + /// The action will execute on one of ClangdServer's internal threads. + /// The AST is only valid for the duration of the callback. + /// As with other actions, the file must have been opened. + void customAction(PathRef File, llvm::StringRef Name, + Callback Action); + /// Returns estimated memory usage and other statistics for each of the /// currently open files. /// Overall memory usage of clangd may be significantly more than reported diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp --- a/clang-tools-extra/clangd/ClangdServer.cpp +++ b/clang-tools-extra/clangd/ClangdServer.cpp @@ -754,6 +754,11 @@ TUScheduler::InvalidateOnUpdate); } +void ClangdServer::customAction(PathRef File, llvm::StringRef Name, + Callback Action) { + WorkScheduler.runWithAST(Name, File, std::move(Action)); +} + llvm::StringMap ClangdServer::fileStats() const { return WorkScheduler.fileStats(); } diff --git a/clang-tools-extra/clangd/unittests/ClangdTests.cpp b/clang-tools-extra/clangd/unittests/ClangdTests.cpp --- a/clang-tools-extra/clangd/unittests/ClangdTests.cpp +++ b/clang-tools-extra/clangd/unittests/ClangdTests.cpp @@ -15,6 +15,7 @@ #include "Matchers.h" #include "SyncAPI.h" #include "TestFS.h" +#include "TestTU.h" #include "URI.h" #include "support/Path.h" #include "support/Threading.h" @@ -28,6 +29,7 @@ #include "llvm/Support/Errc.h" #include "llvm/Support/Path.h" #include "llvm/Support/Regex.h" +#include "llvm/Testing/Support/Error.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include @@ -1141,6 +1143,21 @@ Field(&CodeCompletion::Scope, "ns::")))); } +TEST(ClangdServerTest, CustomAction) { + OverlayCDB CDB(/*Base=*/nullptr); + MockFS FS; + ClangdServer Server(CDB, FS, ClangdServer::optsForTest()); + + Server.addDocument(testPath("foo.cc"), "void x();"); + Decl::Kind XKind = Decl::TranslationUnit; + EXPECT_THAT_ERROR(runCustomAction(Server, testPath("foo.cc"), + [&](InputsAndAST AST) { + XKind = findDecl(AST.AST, "x").getKind(); + }), + llvm::Succeeded()); + EXPECT_EQ(XKind, Decl::Function); +} + // Tests fails when built with asan due to stack overflow. So skip running the // test as a workaround. #if !defined(__has_feature) || !__has_feature(address_sanitizer) diff --git a/clang-tools-extra/clangd/unittests/SyncAPI.h b/clang-tools-extra/clangd/unittests/SyncAPI.h --- a/clang-tools-extra/clangd/unittests/SyncAPI.h +++ b/clang-tools-extra/clangd/unittests/SyncAPI.h @@ -60,6 +60,9 @@ llvm::Expected> runSwitchHeaderSource(ClangdServer &Server, PathRef File); +llvm::Error runCustomAction(ClangdServer &Server, PathRef File, + llvm::function_ref); + } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/unittests/SyncAPI.cpp b/clang-tools-extra/clangd/unittests/SyncAPI.cpp --- a/clang-tools-extra/clangd/unittests/SyncAPI.cpp +++ b/clang-tools-extra/clangd/unittests/SyncAPI.cpp @@ -154,5 +154,20 @@ return std::move(*Result); } +llvm::Error runCustomAction(ClangdServer &Server, PathRef File, + llvm::function_ref Action) { + llvm::Error Result = llvm::Error::success(); + Notification Done; + Server.customAction(File, "Custom", [&](llvm::Expected AST) { + if (!AST) + Result = AST.takeError(); + else + Action(*AST); + Done.notify(); + }); + Done.wait(); + return Result; +} + } // namespace clangd } // namespace clang