kuhnel updated this revision to Diff 355478.
kuhnel added a comment.

using EXPECT_THAT_EXPECTED


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D105177/new/

https://reviews.llvm.org/D105177

Files:
  clang-tools-extra/clangd/CMakeLists.txt
  clang-tools-extra/clangd/index/StdLib.cpp
  clang-tools-extra/clangd/index/StdLib.h
  clang-tools-extra/clangd/unittests/CMakeLists.txt
  clang-tools-extra/clangd/unittests/StdLibIndexTests.cpp

Index: clang-tools-extra/clangd/unittests/StdLibIndexTests.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clangd/unittests/StdLibIndexTests.cpp
@@ -0,0 +1,66 @@
+//===-- StdLibIndexTests.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "index/StdLib.h"
+#include "support/Logger.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Testing/Support/Error.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+using namespace testing;
+
+namespace clang {
+namespace clangd {
+
+/// check that the generated header sources contains some usual standard library
+/// headers
+TEST(StdLibIndexTests, generateIncludeHeader) {
+  auto Sli = StandardLibraryIndex();
+  auto Includes = Sli.generateIncludeHeader();
+
+  EXPECT_THAT(Includes, HasSubstr("#include <string>"));
+  EXPECT_THAT(Includes, HasSubstr("#include <set>"));
+  EXPECT_THAT(Includes, HasSubstr("#include <ostream>"));
+}
+
+/// test building the virtual file system
+TEST(StdLibIndexTests, buildFilesystem) {
+  auto Sli = StandardLibraryIndex();
+  auto FS = Sli.buildFilesystem(Sli.generateIncludeHeader());
+  auto Buffer =
+      FS->getBufferForFile(StandardLibraryIndex::StdLibHeaderFileName);
+  EXPECT_THAT((bool)Buffer.getError(), IsFalse());
+  EXPECT_THAT(Buffer.get()->getBuffer(), HasSubstr("#include <string>"));
+}
+
+/// build the index and check if it contains the right symbols
+TEST(StdLibIndexTests, buildIndex) {
+  auto Sli = StandardLibraryIndex();
+  // TODO: maybe find a way to use a local libcxx for testing if that is
+  //       available on the machine
+  std::string HeaderMock = R"CPP(
+    int myfunc(int a);
+    bool otherfunc(int a, int b);
+  )CPP";
+  auto Index = Sli.indexHeaders(HeaderMock);
+  EXPECT_THAT_EXPECTED(Index, llvm::Succeeded())
+      << llvm::toString(Index.takeError());
+  FuzzyFindRequest Req;
+  Req.Query = "myfunc";
+  Req.AnyScope = true;
+  Req.Limit = 100;
+  std::vector<std::string> Matches;
+  Index.get()->fuzzyFind(
+      Req, [&](const Symbol &Sym) { Matches.push_back(Sym.Name.str()); });
+  EXPECT_THAT(Matches.size(), Eq((size_t)1));
+  EXPECT_THAT(Matches, Contains("myfunc"));
+}
+
+} // namespace clangd
+} // namespace clang
Index: clang-tools-extra/clangd/unittests/CMakeLists.txt
===================================================================
--- clang-tools-extra/clangd/unittests/CMakeLists.txt
+++ clang-tools-extra/clangd/unittests/CMakeLists.txt
@@ -88,6 +88,7 @@
   SemanticSelectionTests.cpp
   SerializationTests.cpp
   SourceCodeTests.cpp
+  StdLibIndexTests.cpp
   SymbolCollectorTests.cpp
   SymbolInfoTests.cpp
   SyncAPI.cpp
Index: clang-tools-extra/clangd/index/StdLib.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clangd/index/StdLib.h
@@ -0,0 +1,42 @@
+//===--- StdLib.h ------------------------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_STDLIB_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_STDLIB_H
+
+#include "index/Index.h"
+#include "index/MemIndex.h"
+#include "support/ThreadsafeFS.h"
+#include <string>
+
+namespace clang {
+namespace clangd {
+
+class StandardLibraryIndex {
+  // TODO: do we really need a class? Or would plain functions be good enough?
+  // TODO: do we really need to make verything public just for unittesting?
+public:
+  /* virtual file name for indexing */
+  static const std::string StdLibHeaderFileName;
+
+  /* generate the index */
+  Expected<std::unique_ptr<SymbolIndex>>
+  indexHeaders(std::string HeaderSources);
+
+  /* generate header containing #includes for all standard library headers */
+  std::string generateIncludeHeader();
+
+  /* build a virtual filesystem with the file to be indexed */
+  llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem>
+  buildFilesystem(std::string HeaderSources);
+};
+
+} // namespace clangd
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_STDLIB_H
\ No newline at end of file
Index: clang-tools-extra/clangd/index/StdLib.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clangd/index/StdLib.cpp
@@ -0,0 +1,152 @@
+//===-- StdLib.cpp ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#include "StdLib.h"
+#include <iostream>
+#include <memory>
+#include <set>
+#include <type_traits>
+
+#include "Compiler.h"
+#include "Headers.h"
+#include "SymbolCollector.h"
+#include "index/IndexAction.h"
+#include "index/Serialization.h"
+#include "support/Logger.h"
+#include "support/ThreadsafeFS.h"
+#include "support/Trace.h"
+#include "clang/Basic/LLVM.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/VirtualFileSystem.h"
+
+namespace clang {
+namespace clangd {
+
+const std::string
+    StandardLibraryIndex::StdLibHeaderFileName("/stdlibheaders.cpp");
+
+std::string StandardLibraryIndex::generateIncludeHeader() {
+  std::string Includes;
+  // gather all the known STL headers in a set for depulication
+  std::set<std::string> Headers;
+
+#define SYMBOL(Name, NameSpace, Header) Headers.insert(#Header);
+#include "StdSymbolMap.inc"
+#undef SYMBOL
+
+  for (auto Header : Headers) {
+    Includes += "#include " + Header + "\n";
+  }
+  return Includes;
+}
+namespace {
+/* Wrapper around llvm::vfs::InMemoryFileSystem */
+class MemTFS : public ThreadsafeFS {
+
+private:
+  const llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS;
+
+public:
+  MemTFS(llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> const FS)
+      : FS(FS){};
+
+  llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> viewImpl() const override {
+    return FS;
+  }
+};
+} // namespace
+
+llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem>
+StandardLibraryIndex::buildFilesystem(std::string HeaderSources) {
+  auto FS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
+  auto Now =
+      std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
+  FS->addFile(StandardLibraryIndex::StdLibHeaderFileName, Now,
+              llvm::MemoryBuffer::getMemBufferCopy(HeaderSources,
+                                                   StdLibHeaderFileName));
+  return FS;
+}
+
+Expected<std::unique_ptr<SymbolIndex>>
+StandardLibraryIndex::indexHeaders(std::string HeaderSources) {
+  ParseInputs Inputs;
+  auto FS = buildFilesystem(HeaderSources);
+  auto TFS = MemTFS(FS);
+  Inputs.TFS = &TFS;
+  // TODO: can we get a real compile command from somewhere?
+  Inputs.CompileCommand = tooling::CompileCommand();
+  Inputs.CompileCommand.Directory = "/";
+  Inputs.CompileCommand.Filename = StdLibHeaderFileName;
+  Inputs.CompileCommand.Output = StdLibHeaderFileName + ".o";
+  Inputs.CompileCommand.CommandLine.push_back("clang++");
+  Inputs.CompileCommand.CommandLine.push_back(Inputs.CompileCommand.Filename);
+  Inputs.CompileCommand.CommandLine.push_back("-o");
+  Inputs.CompileCommand.CommandLine.push_back(Inputs.CompileCommand.Output);
+
+  IgnoreDiagnostics IgnoreDiags;
+
+  auto CI = buildCompilerInvocation(Inputs, IgnoreDiags);
+  if (!CI)
+    return error("Couldn't build compiler invocation");
+
+  auto Buffer = FS->getBufferForFile(StdLibHeaderFileName);
+  if (!Buffer)
+    return error("Could not read file from InMemoryFileSystem");
+  auto Clang = prepareCompilerInstance(std::move(CI), /*Preamble=*/nullptr,
+                                       std::move(*Buffer), FS, IgnoreDiags);
+  if (!Clang)
+    return error("Couldn't build compiler instance");
+
+  SymbolCollector::Options IndexOpts;
+  IndexFileIn IndexSlabs;
+  auto Action = createStaticIndexingAction(
+      IndexOpts, [&](SymbolSlab S) { IndexSlabs.Symbols = std::move(S); },
+      [&](RefSlab R) { IndexSlabs.Refs = std::move(R); },
+      [&](RelationSlab R) { IndexSlabs.Relations = std::move(R); },
+      [&](IncludeGraph IG) { IndexSlabs.Sources = std::move(IG); });
+
+  // We're going to run clang here, and it could potentially crash.
+  // We could use CrashRecoveryContext to try to make indexing crashes nonfatal,
+  // but the leaky "recovery" is pretty scary too in a long-running process.
+  // If crashes are a real problem, maybe we should fork a child process.
+
+  const FrontendInputFile &Input = Clang->getFrontendOpts().Inputs.front();
+  if (!Action->BeginSourceFile(*Clang, Input))
+    return error("BeginSourceFile() failed");
+  if (llvm::Error Err = Action->Execute())
+    return std::move(Err);
+
+  Action->EndSourceFile();
+
+  IndexSlabs.Cmd = Inputs.CompileCommand;
+  assert(IndexSlabs.Symbols && IndexSlabs.Refs && IndexSlabs.Sources &&
+         "Symbols, Refs and Sources must be set.");
+
+  log("Indexed {0} ({1} symbols, {2} refs, {3} files)",
+      Inputs.CompileCommand.Filename, IndexSlabs.Symbols->size(),
+      IndexSlabs.Refs->numRefs(), IndexSlabs.Sources->size());
+
+  trace::Span Tracer("StandardLibraryIndex");
+  SPAN_ATTACH(Tracer, "symbols", int(IndexSlabs.Symbols->size()));
+  SPAN_ATTACH(Tracer, "refs", int(IndexSlabs.Refs->numRefs()));
+  SPAN_ATTACH(Tracer, "sources", int(IndexSlabs.Sources->size()));
+
+  bool HadErrors = Clang->hasDiagnostics() &&
+                   Clang->getDiagnostics().hasUncompilableErrorOccurred();
+  if (HadErrors) {
+    log("Failed to compile standard librarx index, index may be incomplete");
+  }
+
+  auto Index = MemIndex::build(std::move(IndexSlabs.Symbols.getValue()),
+                               std::move(IndexSlabs.Refs.getValue()),
+                               std::move(IndexSlabs.Relations.getValue()));
+
+  return Index;
+}
+} // namespace clangd
+} // namespace clang
Index: clang-tools-extra/clangd/CMakeLists.txt
===================================================================
--- clang-tools-extra/clangd/CMakeLists.txt
+++ clang-tools-extra/clangd/CMakeLists.txt
@@ -110,6 +110,7 @@
   index/Ref.cpp
   index/Relation.cpp
   index/Serialization.cpp
+  index/StdLib.cpp
   index/Symbol.cpp
   index/SymbolCollector.cpp
   index/SymbolID.cpp
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to