[clang-tools-extra] r358571 - [clangd] Include insertion: require header guards, drop other heuristics, treat .def like .inc.

Sam McCall via cfe-commits cfe-commits at lists.llvm.org
Wed Apr 17 03:36:02 PDT 2019


Author: sammccall
Date: Wed Apr 17 03:36:02 2019
New Revision: 358571

URL: http://llvm.org/viewvc/llvm-project?rev=358571&view=rev
Log:
[clangd] Include insertion: require header guards, drop other heuristics, treat .def like .inc.

Summary:
We do have some reports of include insertion behaving badly in some
codebases. Requiring header guards both makes sense in principle, and is
likely to disable this "nice-to-have" feature in codebases where headers don't
follow the expected pattern.

With this we can drop some other heuristics, such as looking at file
extensions to detect known non-headers - implementation files have no guards.

One wrinkle here is #import - objc headers may not have guards because
they're intended to be used via #import. If the header is the main file
or is #included, we won't collect locations - merge should take care of
this if we see the file #imported somewhere. Seems likely to be OK.

Headers which have a canonicalization (stdlib, IWYU) are exempt from this check.
*.inc files continue to be handled by looking up to the including file.
This patch also adds *.def here - tablegen wants this pattern too.

In terms of code structure, the division between SymbolCollector and
CanonicalIncludes has shifted: SymbolCollector is responsible for more.
This is because SymbolCollector has all the SourceManager/HeaderSearch access
needed for checking for guards, and we interleave these checks with the *.def
checks in a loop (potentially).
We could hand all the info into CanonicalIncludes and put the logic there
if that's preferable.

Reviewers: ioeric

Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D60316

Modified:
    clang-tools-extra/trunk/clangd/index/CanonicalIncludes.cpp
    clang-tools-extra/trunk/clangd/index/CanonicalIncludes.h
    clang-tools-extra/trunk/clangd/index/SymbolCollector.cpp
    clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp
    clang-tools-extra/trunk/unittests/clangd/FileIndexTests.cpp
    clang-tools-extra/trunk/unittests/clangd/SymbolCollectorTests.cpp
    clang-tools-extra/trunk/unittests/clangd/TestTU.cpp
    clang-tools-extra/trunk/unittests/clangd/TestTU.h

Modified: clang-tools-extra/trunk/clangd/index/CanonicalIncludes.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/index/CanonicalIncludes.cpp?rev=358571&r1=358570&r2=358571&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/index/CanonicalIncludes.cpp (original)
+++ clang-tools-extra/trunk/clangd/index/CanonicalIncludes.cpp Wed Apr 17 03:36:02 2019
@@ -37,31 +37,12 @@ void CanonicalIncludes::addSymbolMapping
 }
 
 llvm::StringRef
-CanonicalIncludes::mapHeader(llvm::ArrayRef<std::string> Headers,
+CanonicalIncludes::mapHeader(llvm::StringRef Header,
                              llvm::StringRef QualifiedName) const {
-  assert(!Headers.empty());
+  assert(!Header.empty());
   auto SE = SymbolMapping.find(QualifiedName);
   if (SE != SymbolMapping.end())
     return SE->second;
-  // Find the first header such that the extension is not '.inc', and isn't a
-  // recognized non-header file
-  auto I = llvm::find_if(Headers, [](llvm::StringRef Include) {
-    // Skip .inc file whose including header file should
-    // be #included instead.
-    return !Include.endswith(".inc");
-  });
-  if (I == Headers.end())
-    return Headers[0]; // Fallback to the declaring header.
-  llvm::StringRef Header = *I;
-  // If Header is not expected be included (e.g. .cc file), we fall back to
-  // the declaring header.
-  llvm::StringRef Ext = llvm::sys::path::extension(Header).trim('.');
-  // Include-able headers must have precompile type. Treat files with
-  // non-recognized extenstions (TY_INVALID) as headers.
-  auto ExtType = driver::types::lookupTypeForExtension(Ext);
-  if ((ExtType != driver::types::TY_INVALID) &&
-      !driver::types::onlyPrecompileType(ExtType))
-    return Headers[0];
 
   auto MapIt = FullPathMapping.find(Header);
   if (MapIt != FullPathMapping.end())

Modified: clang-tools-extra/trunk/clangd/index/CanonicalIncludes.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/index/CanonicalIncludes.h?rev=358571&r1=358570&r2=358571&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/index/CanonicalIncludes.h (original)
+++ clang-tools-extra/trunk/clangd/index/CanonicalIncludes.h Wed Apr 17 03:36:02 2019
@@ -50,9 +50,9 @@ public:
                         llvm::StringRef CanonicalPath);
 
   /// Returns the canonical include for symbol with \p QualifiedName.
-  /// \p Headers is the include stack: Headers.front() is the file declaring the
-  /// symbol, and Headers.back() is the main file.
-  llvm::StringRef mapHeader(llvm::ArrayRef<std::string> Headers,
+  /// \p Header is the file the declaration was reachable from.
+  /// Header itself will be returned if there is no relevant mapping.
+  llvm::StringRef mapHeader(llvm::StringRef Header,
                             llvm::StringRef QualifiedName) const;
 
 private:

Modified: clang-tools-extra/trunk/clangd/index/SymbolCollector.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/index/SymbolCollector.cpp?rev=358571&r1=358570&r2=358571&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/index/SymbolCollector.cpp (original)
+++ clang-tools-extra/trunk/clangd/index/SymbolCollector.cpp Wed Apr 17 03:36:02 2019
@@ -25,6 +25,7 @@
 #include "clang/Basic/Specifiers.h"
 #include "clang/Index/IndexSymbol.h"
 #include "clang/Index/USRGeneration.h"
+#include "clang/Lex/Preprocessor.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/MemoryBuffer.h"
@@ -127,34 +128,46 @@ bool shouldCollectIncludePath(index::Sym
   }
 }
 
-/// Gets a canonical include (URI of the header or <header>  or "header") for
-/// header of \p Loc.
-/// Returns None if fails to get include header for \p Loc.
+bool isSelfContainedHeader(FileID FID, const SourceManager &SM,
+                           const Preprocessor &PP) {
+  const FileEntry *FE = SM.getFileEntryForID(FID);
+  if (!FE)
+    return false;
+  return PP.getHeaderSearchInfo().isFileMultipleIncludeGuarded(FE);
+}
+
+/// Gets a canonical include (URI of the header or <header> or "header") for
+/// header of \p FID (which should usually be the *expansion* file).
+/// Returns None if includes should not be inserted for this file.
 llvm::Optional<std::string>
 getIncludeHeader(llvm::StringRef QName, const SourceManager &SM,
-                 SourceLocation Loc, const SymbolCollector::Options &Opts) {
-  std::vector<std::string> Headers;
-  // Collect the #include stack.
-  while (true) {
-    if (!Loc.isValid())
-      break;
-    auto FilePath = SM.getFilename(Loc);
-    if (FilePath.empty())
-      break;
-    Headers.push_back(FilePath);
-    if (SM.isInMainFile(Loc))
-      break;
-    Loc = SM.getIncludeLoc(SM.getFileID(Loc));
-  }
-  if (Headers.empty())
-    return None;
-  llvm::StringRef Header = Headers[0];
+                 const Preprocessor &PP, FileID FID,
+                 const SymbolCollector::Options &Opts) {
+  const FileEntry *FE = SM.getFileEntryForID(FID);
+  if (!FE || FE->getName().empty())
+    return llvm::None;
+  llvm::StringRef Filename = FE->getName();
+  // If a file is mapped by canonical headers, use that mapping, regardless
+  // of whether it's an otherwise-good header (header guards etc).
   if (Opts.Includes) {
-    Header = Opts.Includes->mapHeader(Headers, QName);
-    if (Header.startswith("<") || Header.startswith("\""))
-      return Header.str();
+    llvm::StringRef Canonical = Opts.Includes->mapHeader(Filename, QName);
+    // If we had a mapping, always use it.
+    if (Canonical.startswith("<") || Canonical.startswith("\""))
+      return Canonical.str();
+    if (Canonical != Filename)
+      return toURI(SM, Canonical, Opts);
+  }
+  if (!isSelfContainedHeader(FID, SM, PP)) {
+    // A .inc or .def file is often included into a real header to define
+    // symbols (e.g. LLVM tablegen files).
+    if (Filename.endswith(".inc") || Filename.endswith(".def"))
+      return getIncludeHeader(QName, SM, PP,
+                              SM.getFileID(SM.getIncludeLoc(FID)), Opts);
+    // Conservatively refuse to insert #includes to files without guards.
+    return llvm::None;
   }
-  return toURI(SM, Header, Opts);
+  // Standard case: just insert the file itself.
+  return toURI(SM, Filename, Opts);
 }
 
 // Return the symbol range of the token at \p TokLoc.
@@ -439,8 +452,9 @@ bool SymbolCollector::handleMacroOccuren
 
   std::string Include;
   if (Opts.CollectIncludePath && shouldCollectIncludePath(S.SymInfo.Kind)) {
-    if (auto Header = getIncludeHeader(Name->getName(), SM,
-                                       SM.getExpansionLoc(DefLoc), Opts))
+    if (auto Header =
+            getIncludeHeader(Name->getName(), SM, *PP,
+                             SM.getDecomposedExpansionLoc(DefLoc).first, Opts))
       Include = std::move(*Header);
   }
   S.Signature = Signature;
@@ -588,7 +602,8 @@ const Symbol *SymbolCollector::addDeclar
     // Use the expansion location to get the #include header since this is
     // where the symbol is exposed.
     if (auto Header = getIncludeHeader(
-            QName, SM, SM.getExpansionLoc(ND.getLocation()), Opts))
+            QName, SM, *PP,
+            SM.getDecomposedExpansionLoc(ND.getLocation()).first, Opts))
       Include = std::move(*Header);
   }
   if (!Include.empty())

Modified: clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp?rev=358571&r1=358570&r2=358571&view=diff
==============================================================================
--- clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp (original)
+++ clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp Wed Apr 17 03:36:02 2019
@@ -684,6 +684,7 @@ TEST(CompletionTest, DynamicIndexInclude
   ClangdServer Server(CDB, FS, DiagConsumer, Opts);
 
   FS.Files[testPath("foo_header.h")] = R"cpp(
+    #pragma once
     struct Foo {
        // Member doc
        int foo();

Modified: clang-tools-extra/trunk/unittests/clangd/FileIndexTests.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/FileIndexTests.cpp?rev=358571&r1=358570&r2=358571&view=diff
==============================================================================
--- clang-tools-extra/trunk/unittests/clangd/FileIndexTests.cpp (original)
+++ clang-tools-extra/trunk/unittests/clangd/FileIndexTests.cpp Wed Apr 17 03:36:02 2019
@@ -212,33 +212,11 @@ TEST(FileIndexTest, IncludeCollected) {
 }
 
 TEST(FileIndexTest, HasSystemHeaderMappingsInPreamble) {
-  FileIndex Index;
-  const std::string Header = R"cpp(
-    class Foo {};
-  )cpp";
-  auto MainFile = testPath("foo.cpp");
-  auto HeaderFile = testPath("algorithm");
-  std::vector<const char *> Cmd = {"clang", "-xc++", MainFile.c_str(),
-                                   "-include", HeaderFile.c_str()};
-  // Preparse ParseInputs.
-  ParseInputs PI;
-  PI.CompileCommand.Directory = testRoot();
-  PI.CompileCommand.Filename = MainFile;
-  PI.CompileCommand.CommandLine = {Cmd.begin(), Cmd.end()};
-  PI.Contents = "";
-  PI.FS = buildTestFS({{MainFile, ""}, {HeaderFile, Header}});
+  TestTU TU;
+  TU.HeaderCode = "class Foo{};";
+  TU.HeaderFilename = "algorithm";
 
-  // Prepare preamble.
-  auto CI = buildCompilerInvocation(PI);
-  auto PreambleData =
-      buildPreamble(MainFile, *buildCompilerInvocation(PI),
-                    /*OldPreamble=*/nullptr, tooling::CompileCommand(), PI,
-                    /*StoreInMemory=*/true,
-                    [&](ASTContext &Ctx, std::shared_ptr<Preprocessor> PP,
-                        const CanonicalIncludes &Includes) {
-                      Index.updatePreamble(MainFile, Ctx, PP, Includes);
-                    });
-  auto Symbols = runFuzzyFind(Index, "");
+  auto Symbols = runFuzzyFind(*TU.index(), "");
   EXPECT_THAT(Symbols, ElementsAre(_));
   EXPECT_THAT(Symbols.begin()->IncludeHeaders.front().IncludeHeader,
               "<algorithm>");
@@ -369,50 +347,23 @@ TEST(FileIndexTest, CollectMacros) {
 }
 
 TEST(FileIndexTest, ReferencesInMainFileWithPreamble) {
-  const std::string Header = R"cpp(
-    class Foo {};
-  )cpp";
+  TestTU TU;
+  TU.HeaderCode = "class Foo{};";
   Annotations Main(R"cpp(
     #include "foo.h"
     void f() {
       [[Foo]] foo;
     }
   )cpp");
-  auto MainFile = testPath("foo.cpp");
-  auto HeaderFile = testPath("foo.h");
-  std::vector<const char *> Cmd = {"clang", "-xc++", MainFile.c_str()};
-  // Preparse ParseInputs.
-  ParseInputs PI;
-  PI.CompileCommand.Directory = testRoot();
-  PI.CompileCommand.Filename = MainFile;
-  PI.CompileCommand.CommandLine = {Cmd.begin(), Cmd.end()};
-  PI.Contents = Main.code();
-  PI.FS = buildTestFS({{MainFile, Main.code()}, {HeaderFile, Header}});
-
-  // Prepare preamble.
-  auto CI = buildCompilerInvocation(PI);
-  auto PreambleData =
-      buildPreamble(MainFile, *buildCompilerInvocation(PI),
-                    /*OldPreamble=*/nullptr, tooling::CompileCommand(), PI,
-                    /*StoreInMemory=*/true,
-                    [&](ASTContext &Ctx, std::shared_ptr<Preprocessor> PP,
-                        const CanonicalIncludes &) {});
-  // Build AST for main file with preamble.
-  auto AST =
-      ParsedAST::build(createInvocationFromCommandLine(Cmd), PreambleData,
-                       llvm::MemoryBuffer::getMemBufferCopy(Main.code()), PI.FS,
-                       /*Index=*/nullptr, ParseOptions());
-  ASSERT_TRUE(AST);
+  TU.Code = Main.code();
+  auto AST = TU.build();
   FileIndex Index;
-  Index.updateMain(MainFile, *AST);
-
-  auto Foo = findSymbol(TestTU::withHeaderCode(Header).headerSymbols(), "Foo");
-  RefsRequest Request;
-  Request.IDs.insert(Foo.ID);
+  Index.updateMain(testPath(TU.Filename), AST);
 
   // Expect to see references in main file, references in headers are excluded
   // because we only index main AST.
-  EXPECT_THAT(getRefs(Index, Foo.ID), RefsAre({RefRange(Main.range())}));
+  EXPECT_THAT(getRefs(Index, findSymbol(TU.headerSymbols(), "Foo").ID),
+              RefsAre({RefRange(Main.range())}));
 }
 
 } // namespace

Modified: clang-tools-extra/trunk/unittests/clangd/SymbolCollectorTests.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/SymbolCollectorTests.cpp?rev=358571&r1=358570&r2=358571&view=diff
==============================================================================
--- clang-tools-extra/trunk/unittests/clangd/SymbolCollectorTests.cpp (original)
+++ clang-tools-extra/trunk/unittests/clangd/SymbolCollectorTests.cpp Wed Apr 17 03:36:02 2019
@@ -20,6 +20,7 @@
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/VirtualFileSystem.h"
+#include "gmock/gmock-more-matchers.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
@@ -33,7 +34,7 @@ namespace {
 using testing::_;
 using testing::AllOf;
 using testing::Contains;
-using testing::Eq;
+using testing::ElementsAre;
 using testing::Field;
 using testing::IsEmpty;
 using testing::Not;
@@ -58,6 +59,7 @@ MATCHER_P(DeclURI, P, "") {
   return StringRef(arg.CanonicalDeclaration.FileURI) == P;
 }
 MATCHER_P(DefURI, P, "") { return StringRef(arg.Definition.FileURI) == P; }
+MATCHER(IncludeHeader, "") { return !arg.IncludeHeaders.empty(); }
 MATCHER_P(IncludeHeader, P, "") {
   return (arg.IncludeHeaders.size() == 1) &&
          (arg.IncludeHeaders.begin()->IncludeHeader == P);
@@ -249,6 +251,8 @@ public:
     TestFileURI = URI::create(TestFileName).toString();
   }
 
+  // Note that unlike TestTU, no automatic header guard is added.
+  // HeaderCode should start with #pragma once to be treated as modular.
   bool runSymbolCollector(llvm::StringRef HeaderCode, llvm::StringRef MainCode,
                           const std::vector<std::string> &ExtraArgs = {}) {
     llvm::IntrusiveRefCntPtr<FileManager> Files(
@@ -920,7 +924,7 @@ TEST_F(SymbolCollectorTest, Snippet) {
 
 TEST_F(SymbolCollectorTest, IncludeHeaderSameAsFileURI) {
   CollectorOpts.CollectIncludePath = true;
-  runSymbolCollector("class Foo {};", /*Main=*/"");
+  runSymbolCollector("#pragma once\nclass Foo {};", /*Main=*/"");
   EXPECT_THAT(Symbols, UnorderedElementsAre(
                            AllOf(QName("Foo"), DeclURI(TestHeaderURI))));
   EXPECT_THAT(Symbols.begin()->IncludeHeaders,
@@ -1020,53 +1024,54 @@ TEST_F(SymbolCollectorTest, SkipIncFileW
 
 TEST_F(SymbolCollectorTest, MainFileIsHeaderWhenSkipIncFile) {
   CollectorOpts.CollectIncludePath = true;
-  CanonicalIncludes Includes;
-  CollectorOpts.Includes = &Includes;
-  TestFileName = testPath("main.h");
+  // To make this case as hard as possible, we won't tell clang main is a
+  // header. No extension, no -x c++-header.
+  TestFileName = testPath("no_ext_main");
   TestFileURI = URI::create(TestFileName).toString();
   auto IncFile = testPath("test.inc");
   auto IncURI = URI::create(IncFile).toString();
   InMemoryFileSystem->addFile(IncFile, 0,
                               llvm::MemoryBuffer::getMemBuffer("class X {};"));
-  runSymbolCollector("", /*Main=*/"#include \"test.inc\"",
+  runSymbolCollector("", R"cpp(
+    // Can't use #pragma once in a main file clang doesn't think is a header.
+    #ifndef MAIN_H_
+    #define MAIN_H_
+    #include "test.inc"
+    #endif
+  )cpp",
                      /*ExtraArgs=*/{"-I", testRoot()});
   EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(QName("X"), DeclURI(IncURI),
                                                   IncludeHeader(TestFileURI))));
 }
 
-TEST_F(SymbolCollectorTest, MainFileIsHeaderWithoutExtensionWhenSkipIncFile) {
+TEST_F(SymbolCollectorTest, IncFileInNonHeader) {
   CollectorOpts.CollectIncludePath = true;
-  CanonicalIncludes Includes;
-  CollectorOpts.Includes = &Includes;
-  TestFileName = testPath("no_ext_main");
+  TestFileName = testPath("main.cc");
   TestFileURI = URI::create(TestFileName).toString();
   auto IncFile = testPath("test.inc");
   auto IncURI = URI::create(IncFile).toString();
   InMemoryFileSystem->addFile(IncFile, 0,
                               llvm::MemoryBuffer::getMemBuffer("class X {};"));
-  runSymbolCollector("", /*Main=*/"#include \"test.inc\"",
+  runSymbolCollector("", R"cpp(
+    #include "test.inc"
+  )cpp",
                      /*ExtraArgs=*/{"-I", testRoot()});
   EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(QName("X"), DeclURI(IncURI),
-                                                  IncludeHeader(TestFileURI))));
+                                                  Not(IncludeHeader()))));
 }
 
-TEST_F(SymbolCollectorTest, FallbackToIncFileWhenIncludingFileIsCC) {
-  CollectorOpts.CollectIncludePath = true;
-  CanonicalIncludes Includes;
-  CollectorOpts.Includes = &Includes;
-  auto IncFile = testPath("test.inc");
-  auto IncURI = URI::create(IncFile).toString();
-  InMemoryFileSystem->addFile(IncFile, 0,
-                              llvm::MemoryBuffer::getMemBuffer("class X {};"));
-  runSymbolCollector("", /*Main=*/"#include \"test.inc\"",
-                     /*ExtraArgs=*/{"-I", testRoot()});
-  EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(QName("X"), DeclURI(IncURI),
-                                                  IncludeHeader(IncURI))));
+TEST_F(SymbolCollectorTest, NonModularHeader) {
+  auto TU = TestTU::withHeaderCode("int x();");
+  EXPECT_THAT(TU.headerSymbols(), ElementsAre(IncludeHeader()));
+
+  TU.ImplicitHeaderGuard = false;
+  EXPECT_THAT(TU.headerSymbols(), ElementsAre(Not(IncludeHeader())));
 }
 
 TEST_F(SymbolCollectorTest, AvoidUsingFwdDeclsAsCanonicalDecls) {
   CollectorOpts.CollectIncludePath = true;
   Annotations Header(R"(
+    #pragma once
     // Forward declarations of TagDecls.
     class C;
     struct S;
@@ -1100,7 +1105,8 @@ TEST_F(SymbolCollectorTest, AvoidUsingFw
 
 TEST_F(SymbolCollectorTest, ClassForwardDeclarationIsCanonical) {
   CollectorOpts.CollectIncludePath = true;
-  runSymbolCollector(/*Header=*/"class X;", /*Main=*/"class X {};");
+  runSymbolCollector(/*Header=*/"#pragma once\nclass X;",
+                     /*Main=*/"class X {};");
   EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(
                            QName("X"), DeclURI(TestHeaderURI),
                            IncludeHeader(TestHeaderURI), DefURI(TestFileURI))));
@@ -1167,6 +1173,7 @@ TEST_F(SymbolCollectorTest, Origin) {
 TEST_F(SymbolCollectorTest, CollectMacros) {
   CollectorOpts.CollectIncludePath = true;
   Annotations Header(R"(
+    #pragma once
     #define X 1
     #define $mac[[MAC]](x) int x
     #define $used[[USED]](y) float y;

Modified: clang-tools-extra/trunk/unittests/clangd/TestTU.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/TestTU.cpp?rev=358571&r1=358570&r2=358571&view=diff
==============================================================================
--- clang-tools-extra/trunk/unittests/clangd/TestTU.cpp (original)
+++ clang-tools-extra/trunk/unittests/clangd/TestTU.cpp Wed Apr 17 03:36:02 2019
@@ -19,13 +19,19 @@ namespace clangd {
 
 ParsedAST TestTU::build() const {
   std::string FullFilename = testPath(Filename),
-              FullHeaderName = testPath(HeaderFilename);
+              FullHeaderName = testPath(HeaderFilename),
+              ImportThunk = testPath("import_thunk.h");
   std::vector<const char *> Cmd = {"clang", FullFilename.c_str()};
+  // We want to implicitly include HeaderFilename without messing up offsets.
+  // -include achieves this, but sometimes we want #import (to simulate a header
+  // guard without messing up offsets). In this case, use an intermediate file.
+  std::string ThunkContents = "#import \"" + FullHeaderName + "\"\n";
   // FIXME: this shouldn't need to be conditional, but it breaks a
   // GoToDefinition test for some reason (getMacroArgExpandedLocation fails).
   if (!HeaderCode.empty()) {
     Cmd.push_back("-include");
-    Cmd.push_back(FullHeaderName.c_str());
+    Cmd.push_back(ImplicitHeaderGuard ? ImportThunk.c_str()
+                                      : FullHeaderName.c_str());
   }
   Cmd.insert(Cmd.end(), ExtraArgs.begin(), ExtraArgs.end());
   ParseInputs Inputs;
@@ -33,7 +39,9 @@ ParsedAST TestTU::build() const {
   Inputs.CompileCommand.CommandLine = {Cmd.begin(), Cmd.end()};
   Inputs.CompileCommand.Directory = testRoot();
   Inputs.Contents = Code;
-  Inputs.FS = buildTestFS({{FullFilename, Code}, {FullHeaderName, HeaderCode}});
+  Inputs.FS = buildTestFS({{FullFilename, Code},
+                           {FullHeaderName, HeaderCode},
+                           {ImportThunk, ThunkContents}});
   Inputs.Opts = ParseOptions();
   Inputs.Opts.ClangTidyOpts.Checks = ClangTidyChecks;
   Inputs.Index = ExternalIndex;

Modified: clang-tools-extra/trunk/unittests/clangd/TestTU.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/TestTU.h?rev=358571&r1=358570&r2=358571&view=diff
==============================================================================
--- clang-tools-extra/trunk/unittests/clangd/TestTU.h (original)
+++ clang-tools-extra/trunk/unittests/clangd/TestTU.h Wed Apr 17 03:36:02 2019
@@ -52,6 +52,9 @@ struct TestTU {
   // Index to use when building AST.
   const SymbolIndex *ExternalIndex = nullptr;
 
+  // Simulate a header guard of the header (using an #import directive).
+  bool ImplicitHeaderGuard = true;
+
   ParsedAST build() const;
   SymbolSlab headerSymbols() const;
   std::unique_ptr<SymbolIndex> index() const;




More information about the cfe-commits mailing list