kadircet created this revision.
kadircet added a reviewer: sammccall.
Herald added subscribers: cfe-commits, usaxena95, arphaman, mgrang, jkorous, 
MaskRay, ilya-biryukov.
Herald added a project: clang.

Depends on D77392 <https://reviews.llvm.org/D77392>.

Enables building ASTs with stale preambles by handling additional preamble
includes. Sets the correct location information for those imaginary includes so
that features like gotodef/documentlink keeps functioning propoerly.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D77644

Files:
  clang-tools-extra/clangd/Compiler.h
  clang-tools-extra/clangd/ParsedAST.cpp
  clang-tools-extra/clangd/unittests/ParsedASTTests.cpp

Index: clang-tools-extra/clangd/unittests/ParsedASTTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/ParsedASTTests.cpp
+++ clang-tools-extra/clangd/unittests/ParsedASTTests.cpp
@@ -17,7 +17,9 @@
 #include "Annotations.h"
 #include "Compiler.h"
 #include "Diagnostics.h"
+#include "Headers.h"
 #include "ParsedAST.h"
+#include "Preamble.h"
 #include "SourceCode.h"
 #include "TestFS.h"
 #include "TestTU.h"
@@ -28,6 +30,7 @@
 #include "clang/Lex/PPCallbacks.h"
 #include "clang/Lex/Token.h"
 #include "clang/Tooling/Syntax/Tokens.h"
+#include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/ScopedPrinter.h"
 #include "gmock/gmock-matchers.h"
@@ -82,6 +85,13 @@
   return arg.beginOffset() == R.Begin && arg.endOffset() == R.End;
 }
 
+MATCHER(EqInc, "") {
+  Inclusion Actual = testing::get<0>(arg);
+  Inclusion Expected = testing::get<1>(arg);
+  return std::tie(Actual.HashOffset, Actual.R, Actual.Written) ==
+         std::tie(Expected.HashOffset, Expected.R, Expected.Written);
+}
+
 TEST(ParsedASTTest, TopLevelDecls) {
   TestTU TU;
   TU.HeaderCode = R"(
@@ -420,6 +430,73 @@
   }
 }
 
+TEST(ParsedASTTest, AdditionalIncludes) {
+  TestTU TU;
+  TU.Filename = "foo.cpp";
+  TU.AdditionalFiles["foo.h"] = "void foo();";
+  TU.AdditionalFiles["sub/baz.h"] = "void baz();";
+  TU.AdditionalFiles["sub/aux.h"] = "void aux();";
+  TU.ExtraArgs = {"-I" + testPath("sub")};
+  TU.Code = R"cpp(
+    #include "baz.h"
+    #include "foo.h"
+    #include "sub/aux.h"
+    void bar() {
+      foo();
+      baz();
+      aux();
+    })cpp";
+  auto ExpectedAST = TU.build();
+
+  // Build preamble with no includes.
+  TU.Code = R"cpp(
+    void bar() {
+      foo();
+      baz();
+      aux();
+    })cpp";
+  StoreDiags Diags;
+  auto Inputs = TU.inputs();
+  auto CI = buildCompilerInvocation(Inputs, Diags);
+  auto Preamble = buildPreamble("foo.cpp", *CI, Inputs, true, nullptr);
+  ASSERT_TRUE(Preamble);
+  EXPECT_THAT(Preamble->Includes.MainFileIncludes, testing::IsEmpty());
+
+  // Now build an AST using additional includes and check that locations are
+  // correctly parsed.
+  TU.Code = R"cpp(
+    #include "baz.h"
+    #include "foo.h"
+    #include "sub/aux.h"
+    void bar() {
+      foo();
+      baz();
+      aux();
+    })cpp";
+  Inputs = TU.inputs();
+  Inputs.Opts.AdditionalIncludes =
+      getPreambleIncludes(TU.Code, ExpectedAST.getLangOpts());
+  auto PatchedAST = buildAST("foo.cpp", std::move(CI), {}, Inputs, Preamble);
+  ASSERT_TRUE(PatchedAST);
+
+  // Ensure source location information is correct.
+  EXPECT_THAT(PatchedAST->getIncludeStructure().MainFileIncludes,
+              testing::Pointwise(
+                  EqInc(), ExpectedAST.getIncludeStructure().MainFileIncludes));
+  auto StringMapToVector = [](const llvm::StringMap<unsigned> SM) {
+    std::vector<std::pair<std::string, unsigned>> Res;
+    for (const auto &E : SM)
+      Res.push_back({E.first().str(), E.second});
+    llvm::sort(Res);
+    return Res;
+  };
+  // Ensure file proximity signals are correct.
+  EXPECT_EQ(StringMapToVector(PatchedAST->getIncludeStructure().includeDepth(
+                testPath("foo.cpp"))),
+            StringMapToVector(ExpectedAST.getIncludeStructure().includeDepth(
+                testPath("foo.cpp"))));
+}
+
 } // namespace
 } // namespace clangd
 } // namespace clang
Index: clang-tools-extra/clangd/ParsedAST.cpp
===================================================================
--- clang-tools-extra/clangd/ParsedAST.cpp
+++ clang-tools-extra/clangd/ParsedAST.cpp
@@ -262,6 +262,15 @@
   std::string Filename =
       std::string(Buffer->getBufferIdentifier()); // Absolute.
 
+  // Process additional includes as implicit includes to ensure AST is built
+  // with necessary symbol information.
+  for (const auto &Inc : Opts.AdditionalIncludes) {
+    // Inc.Written contains quotes or angles, preprocessor requires them to be
+    // stripped.
+    CI->getPreprocessorOpts().Includes.emplace_back(
+        llvm::StringRef(Inc.Written).drop_back().drop_front());
+  }
+
   auto Clang = prepareCompilerInstance(std::move(CI), PreamblePCH,
                                        std::move(Buffer), VFS, ASTDiags);
   if (!Clang)
@@ -431,6 +440,31 @@
     std::vector<Diag> D = ASTDiags.take(CTContext.getPointer());
     Diags.insert(Diags.end(), D.begin(), D.end());
   }
+
+  // Add location information for additional includes.
+  auto &SM = Clang->getSourceManager();
+  auto FID = SM.getMainFileID();
+  const auto *Includer = SM.getFileEntryForID(FID);
+  auto FLoc = SM.getLocForStartOfFile(FID);
+  for (auto &Inc : Opts.AdditionalIncludes) {
+    Includes.MainFileIncludes.push_back(Inc);
+
+    // Try to resolve it using header search.
+    const DirectoryLookup *CurDir = nullptr;
+    auto IncLoc = FLoc.getLocWithOffset(Inc.HashOffset);
+    auto FER = Clang->getPreprocessor().getHeaderSearchInfo().LookupFile(
+        llvm::StringRef(Inc.Written).drop_front().drop_back(), IncLoc,
+        Inc.Written.front() == '<', nullptr, CurDir,
+        {{Includer, Includer->getDir()}}, nullptr, nullptr, nullptr, nullptr,
+        nullptr, nullptr);
+    if (!FER)
+      continue;
+    auto &FE = FER->getFileEntry();
+    Includes.MainFileIncludes.back().Resolved = FE.tryGetRealPathName().str();
+    Includes.recordInclude(Includer->getName(), FE.getName(),
+                           FE.tryGetRealPathName());
+  }
+
   return ParsedAST(Version, std::move(Preamble), std::move(Clang),
                    std::move(Action), std::move(Tokens), std::move(Macros),
                    std::move(ParsedDecls), std::move(Diags),
Index: clang-tools-extra/clangd/Compiler.h
===================================================================
--- clang-tools-extra/clangd/Compiler.h
+++ clang-tools-extra/clangd/Compiler.h
@@ -17,10 +17,12 @@
 
 #include "../clang-tidy/ClangTidyOptions.h"
 #include "GlobalCompilationDatabase.h"
+#include "Headers.h"
 #include "index/Index.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/PrecompiledPreamble.h"
 #include "clang/Tooling/CompilationDatabase.h"
+#include <vector>
 
 namespace clang {
 namespace clangd {
@@ -39,6 +41,7 @@
   tidy::ClangTidyOptions ClangTidyOpts;
   bool SuggestMissingIncludes = false;
   bool BuildRecoveryAST = false;
+  std::vector<Inclusion> AdditionalIncludes;
 };
 
 /// Information required to run clang, e.g. to parse AST or do code completion.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to