kuhnel updated this revision to Diff 367737.
kuhnel marked an inline comment as done.
kuhnel added a comment.

addressed code review comments

also fixed use-after-free


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/ClangdServer.cpp
  clang-tools-extra/clangd/FS.cpp
  clang-tools-extra/clangd/FS.h
  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,60 @@
+//===-- 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 "FuzzyMatch.h"
+#include "TestFS.h"
+#include "TestIndex.h"
+#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"
+#include <memory>
+
+using namespace testing;
+
+namespace clang {
+namespace clangd {
+
+/// check that the generated header sources contains some usual standard library
+/// headers
+TEST(StdLibIndexTests, generateUmbrellaHeader) {
+  MockFS FS;
+  auto UmbrellaHeader = generateUmbrellaHeaders(StandardLibrarVariant::CXX14);
+
+  EXPECT_THAT(UmbrellaHeader, HasSubstr("#include <string>"));
+  EXPECT_THAT(UmbrellaHeader, HasSubstr("#include <set>"));
+  EXPECT_THAT(UmbrellaHeader, HasSubstr("#include <ostream>"));
+}
+
+/// build the index and check if it contains the right symbols
+TEST(StdLibIndexTests, buildIndex) {
+  MockFS FS;
+  // 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 =
+      indexUmbrellaHeaders(HeaderMock, FS, StandardLibrarVariant::CXX14);
+  ASSERT_TRUE(Index != nullptr);
+
+  FuzzyFindRequest Req;
+  Req.AnyScope = true;
+  EXPECT_THAT(match(*Index, Req),
+              UnorderedElementsAre(llvm::StringRef("myfunc"),
+                                   llvm::StringRef("otherfunc")));
+}
+
+// TODO: add tests for indexStandardLibrary()
+// TODO: test with different library versions
+
+} // 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
@@ -79,6 +79,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,55 @@
+//===--- 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
+//
+//===----------------------------------------------------------------------===//
+// Clangd indexer for the C++ standard library.
+//
+// The index only contains symbols that are part of the translation unit. So
+// if your translation unit does not yet #include <string>, you do not get
+// auto completion for std::string. However we expect that many users would
+// like to use the the standard library anyway, so we could index that by
+// default an offer e.g. code completion without requiring #includes.
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_STDLIB_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_STDLIB_H
+
+#include "FS.h"
+#include "index/Index.h"
+#include "index/MemIndex.h"
+#include "support/ThreadsafeFS.h"
+#include "clang/AST/Expr.h"
+#include "llvm/ADT/StringRef.h"
+#include <string>
+
+namespace clang {
+namespace clangd {
+
+// Enumeration of supported Standard Library versions.
+// FIXME: support muiltiple languages (e.g. C and C++) and versions (e.g. 11,
+// 14, 17) of the standard library.
+// FIXME: add heuristic to detect this version somehow (magically).
+enum class StandardLibrarVariant { CXX14 = 0 };
+
+/// Generate a index of the standard library index for a given variant of
+/// the standard library. This index can be included if the translation unit
+/// does not (yet) contain any standard library headers.
+std::unique_ptr<SymbolIndex> indexStandardLibrary(const ThreadsafeFS &TFS);
+
+/// Index the given umbrella header file using the standard library from the
+/// given file system.
+std::unique_ptr<SymbolIndex>
+indexUmbrellaHeaders(llvm::StringRef HeaderSources, const ThreadsafeFS &TFS,
+                     const StandardLibrarVariant &LibraryVariant);
+
+/// generate header containing #includes for all standard library headers
+llvm::StringRef
+generateUmbrellaHeaders(const StandardLibrarVariant &LibraryVariant);
+
+} // 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,150 @@
+//===-- 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 <fstream>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "Compiler.h"
+#include "FS.h"
+#include "SymbolCollector.h"
+#include "dex/Dex.h"
+#include "index/Index.h"
+#include "index/IndexAction.h"
+#include "index/Ref.h"
+#include "index/Serialization.h"
+#include "support/Logger.h"
+#include "support/ThreadsafeFS.h"
+#include "support/Trace.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/ADT/None.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/MemoryBuffer.h"
+
+namespace clang {
+namespace clangd {
+
+std::unique_ptr<SymbolIndex> indexStandardLibrary(const ThreadsafeFS &TFS) {
+  // FIXME: this should be replace with some heuristic to detect the current
+  // language variant.
+  auto Variant = StandardLibrarVariant::CXX14;
+  return indexUmbrellaHeaders(generateUmbrellaHeaders(Variant), TFS, Variant);
+}
+// build umbrelle header for the the standard library.
+// FIXME: consider different library versions, based on `LibraryVersion`
+llvm::StringRef
+generateUmbrellaHeaders(const StandardLibrarVariant &LibraryVariant) {
+
+  // bild the string only once and cache the results in `*Once`.
+  static std::string *Once = [] {
+    std::vector<llvm::StringLiteral> Headers;
+    std::string *Result = new std::string;
+
+#define SYMBOL(Name, NameSpace, Header) Headers.push_back(#Header);
+#include "StdSymbolMap.inc"
+#undef SYMBOL
+
+    // deduplication of the headers
+    llvm::sort(Headers.begin(), Headers.end());
+    auto Last = std::unique(Headers.begin(), Headers.end());
+    for (auto Header = Headers.begin(); Header != Last; ++Header) {
+      *Result += "#include " + Header->str() + "\n";
+    }
+    return Result;
+  }();
+  return llvm::StringRef(*Once);
+}
+
+std::unique_ptr<SymbolIndex>
+indexUmbrellaHeaders(llvm::StringRef HeaderSources, const ThreadsafeFS &TFS,
+                     const StandardLibrarVariant &LibraryVariant) {
+  trace::Span Tracer("StandardLibraryIndex");
+  ParseInputs Inputs;
+  Inputs.TFS = &TFS;
+  llvm::SmallString<64> UmbrellaHeaderFilename = virtualRoot();
+  llvm::sys::path::append(UmbrellaHeaderFilename, "UmbrellaHeader.h");
+
+  // TODO: can we get a real compile command from somewhere?
+  Inputs.CompileCommand.Directory = virtualRoot().str();
+  Inputs.CompileCommand.Filename = UmbrellaHeaderFilename.c_str();
+  Inputs.CompileCommand.CommandLine.push_back("clang++");
+
+  // FIXME: add flags more language variants
+  if (LibraryVariant == StandardLibrarVariant::CXX14) {
+    Inputs.CompileCommand.CommandLine.push_back("-xc++-header");
+    Inputs.CompileCommand.CommandLine.push_back("-std=c++14");
+  } else {
+    elog("Unsupported language variant. Not indexing standard library.");
+    return nullptr;
+  }
+
+  Inputs.CompileCommand.CommandLine.push_back(Inputs.CompileCommand.Filename);
+
+  IgnoreDiagnostics IgnoreDiags;
+
+  auto CI = buildCompilerInvocation(Inputs, IgnoreDiags);
+  if (!CI) {
+    elog("Standard Library Index: Couldn't build compiler invocation");
+    return nullptr;
+  }
+
+  auto Buffer = llvm::MemoryBuffer::getMemBuffer(
+      HeaderSources, Inputs.CompileCommand.Filename);
+  assert(Buffer && Buffer->getBufferSize() > 0);
+  auto Clang = prepareCompilerInstance(
+      std::move(CI), /*Preamble=*/nullptr, std::move(Buffer),
+      TFS.view(/*CWD=*/llvm::None), IgnoreDiags);
+  if (!Clang) {
+    elog("tandard Library Index: Couldn't build compiler instance");
+    return nullptr;
+  }
+  SymbolCollector::Options IndexOpts;
+  IndexFileIn IndexSlabs;
+
+  // we only care about the symbols, so not storing the other attributes
+  auto Action = createStaticIndexingAction(
+      IndexOpts, [&](SymbolSlab S) { IndexSlabs.Symbols = std::move(S); },
+      nullptr, nullptr, nullptr);
+
+  const FrontendInputFile &Input = Clang->getFrontendOpts().Inputs.front();
+  if (!Action->BeginSourceFile(*Clang, Input)) {
+    elog("Standard Library Index:BeginSourceFile() failed");
+    return nullptr;
+  }
+
+  if (llvm::Error Err = Action->Execute()) {
+    elog(toString(std::move(Err)).c_str());
+    return nullptr;
+  }
+
+  Action->EndSourceFile();
+
+  assert(IndexSlabs.Symbols);
+  log("Indexed standard library: {0} ({1} symbols)",
+      Inputs.CompileCommand.Filename, IndexSlabs.Symbols->size());
+
+  SPAN_ATTACH(Tracer, "symbols", int(IndexSlabs.Symbols->size()));
+
+  bool HadErrors = Clang->hasDiagnostics() &&
+                   Clang->getDiagnostics().hasUncompilableErrorOccurred();
+  if (HadErrors) {
+    log("Errors when generating the standard library index, index may be "
+        "incomplete");
+  }
+
+  // FIXME: filter Symbols to only include those in our list, rather than
+  //        all the private implementation cruft. (I expect cruft is the
+  //        majority by weight)
+
+  auto Index = dex::Dex::build(std::move(IndexSlabs.Symbols.getValue()),
+                               RefSlab(), RelationSlab());
+  return Index;
+}
+} // namespace clangd
+} // namespace clang
Index: clang-tools-extra/clangd/FS.h
===================================================================
--- clang-tools-extra/clangd/FS.h
+++ clang-tools-extra/clangd/FS.h
@@ -74,6 +74,10 @@
 /// filtering everything we get over LSP, CDB, etc.
 Path removeDots(PathRef File);
 
+/// Get a virtual root directory for the filesystem depending on the OS
+/// This is useful when a plausible absolute path name is needed.
+const llvm::StringLiteral virtualRoot();
+
 } // namespace clangd
 } // namespace clang
 
Index: clang-tools-extra/clangd/FS.cpp
===================================================================
--- clang-tools-extra/clangd/FS.cpp
+++ clang-tools-extra/clangd/FS.cpp
@@ -117,5 +117,15 @@
   return CanonPath.str().str();
 }
 
+const llvm::StringLiteral virtualRoot() {
+#ifdef _WIN32
+  return "C:\\virtual\\";
+#else
+  // This path must exist on the current file system. The only place we can
+  // safely assume to exist is "/".
+  return "/";
+#endif
+}
+
 } // namespace clangd
 } // namespace clang
Index: clang-tools-extra/clangd/ClangdServer.cpp
===================================================================
--- clang-tools-extra/clangd/ClangdServer.cpp
+++ clang-tools-extra/clangd/ClangdServer.cpp
@@ -895,7 +895,7 @@
           // It's safe to pass in the TU, as dumpAST() does not
           // deserialize the preamble.
           auto Node = DynTypedNode::create(
-                *Inputs->AST.getASTContext().getTranslationUnitDecl());
+              *Inputs->AST.getASTContext().getTranslationUnitDecl());
           return CB(dumpAST(Node, Inputs->AST.getTokens(),
                             Inputs->AST.getASTContext()));
         }
Index: clang-tools-extra/clangd/CMakeLists.txt
===================================================================
--- clang-tools-extra/clangd/CMakeLists.txt
+++ clang-tools-extra/clangd/CMakeLists.txt
@@ -118,6 +118,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