dgoldman updated this revision to Diff 399336.
dgoldman added a comment.

Run clang-format


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D117056

Files:
  clang-tools-extra/clangd/index/SymbolCollector.cpp
  clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
  clang/lib/Lex/HeaderSearch.cpp

Index: clang/lib/Lex/HeaderSearch.cpp
===================================================================
--- clang/lib/Lex/HeaderSearch.cpp
+++ clang/lib/Lex/HeaderSearch.cpp
@@ -1049,6 +1049,11 @@
             getUniqueFrameworkName(StringRef(Filename.begin(), SlashPos));
       if (CurDir->isIndexHeaderMap())
         HFI.IndexHeaderMapHeader = 1;
+    } else if (CurDir->isFramework()) {
+      size_t SlashPos = Filename.find('/');
+      if (SlashPos != StringRef::npos)
+        HFI.Framework =
+            getUniqueFrameworkName(StringRef(Filename.begin(), SlashPos));
     }
 
     if (checkMSVCHeaderSearch(Diags, MSFE ? &MSFE->getFileEntry() : nullptr,
Index: clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
+++ clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
@@ -663,6 +663,109 @@
                   QName("Cat::meow"), QName("Cat::pur")));
 }
 
+TEST_F(SymbolCollectorTest, ObjCFrameworkIncludeHeader) {
+  CollectorOpts.CollectIncludePath = true;
+  auto FrameworksPath = testPath("Frameworks/");
+  auto FrameworkHeaderPath =
+      testPath("Frameworks/Foundation.framework/Headers/NSObject.h");
+  const std::string FrameworkHeader = R"(
+    __attribute((objc_root_class))
+    @interface NSObject
+    @end
+  )";
+  InMemoryFileSystem->addFile(
+      FrameworkHeaderPath, 0,
+      llvm::MemoryBuffer::getMemBuffer(FrameworkHeader));
+
+  const std::string Header = R"(
+    #import <Foundation/NSObject.h>
+
+    @interface Container : NSObject
+    @end
+  )";
+  const std::string Main = "";
+  TestFileName = testPath("test.m");
+  runSymbolCollector(Header, Main, {"-F", FrameworksPath, "-xobjective-c++"});
+  EXPECT_THAT(Symbols, UnorderedElementsAre(
+                           AllOf(QName("NSObject"),
+                                 IncludeHeader("\"Foundation/NSObject.h\"")),
+                           AllOf(QName("Container"))));
+}
+
+TEST_F(SymbolCollectorTest, ObjCUmbrellaFrameworkIncludeHeader) {
+  CollectorOpts.CollectIncludePath = true;
+  auto FrameworksPath = testPath("Frameworks/");
+  auto UmbrellaHeaderPath =
+      testPath("Frameworks/Foundation.framework/Headers/Foundation.h");
+  const std::string UmbrellaHeader = R"(
+    #import <Foundation/NSObject.h>
+  )";
+  auto FrameworkHeaderPath =
+      testPath("Frameworks/Foundation.framework/Headers/NSObject.h");
+  const std::string FrameworkHeader = R"(
+    __attribute((objc_root_class))
+    @interface NSObject
+    @end
+  )";
+  InMemoryFileSystem->addFile(UmbrellaHeaderPath, 0,
+                              llvm::MemoryBuffer::getMemBuffer(UmbrellaHeader));
+  InMemoryFileSystem->addFile(
+      FrameworkHeaderPath, 0,
+      llvm::MemoryBuffer::getMemBuffer(FrameworkHeader));
+
+  const std::string Header = R"(
+    #import <Foundation/Foundation.h>
+
+    @interface Container : NSObject
+    @end
+  )";
+  const std::string Main = "";
+  TestFileName = testPath("test.m");
+  runSymbolCollector(Header, Main, {"-F", FrameworksPath, "-xobjective-c++"});
+  EXPECT_THAT(Symbols, UnorderedElementsAre(
+                           AllOf(QName("NSObject"),
+                                 IncludeHeader("\"Foundation/Foundation.h\"")),
+                           AllOf(QName("Container"))));
+}
+
+TEST_F(SymbolCollectorTest, ObjCSystemUmbrellaFrameworkIncludeHeader) {
+  CollectorOpts.CollectIncludePath = true;
+  auto FrameworksPath = testPath("Frameworks/");
+  auto UmbrellaHeaderPath =
+      testPath("Frameworks/Foundation.framework/Headers/Foundation.h");
+  const std::string UmbrellaHeader = R"(
+    #import <Foundation/NSObject.h>
+  )";
+  auto FrameworkHeaderPath =
+      testPath("Frameworks/Foundation.framework/Headers/NSObject.h");
+  const std::string FrameworkHeader = R"(
+    __attribute((objc_root_class))
+    @interface NSObject
+    @end
+  )";
+  InMemoryFileSystem->addFile(UmbrellaHeaderPath, 0,
+                              llvm::MemoryBuffer::getMemBuffer(UmbrellaHeader));
+  InMemoryFileSystem->addFile(
+      FrameworkHeaderPath, 0,
+      llvm::MemoryBuffer::getMemBuffer(FrameworkHeader));
+
+  const std::string Header = R"(
+    #import <Foundation/Foundation.h>
+
+    @interface Container : NSObject
+    @end
+  )";
+  const std::string Main = "";
+  TestFileName = testPath("test.m");
+  runSymbolCollector(
+      Header, Main,
+      {"-iframeworkwithsysroot", FrameworksPath, "-xobjective-c++"});
+  EXPECT_THAT(Symbols, UnorderedElementsAre(
+                           AllOf(QName("NSObject"),
+                                 IncludeHeader("<Foundation/Foundation.h>")),
+                           AllOf(QName("Container"))));
+}
+
 TEST_F(SymbolCollectorTest, Locations) {
   Annotations Header(R"cpp(
     // Declared in header, defined in main.
@@ -912,12 +1015,12 @@
     llvm::StringRef Main;
     llvm::StringRef TargetSymbolName;
   } TestCases[] = {
-    {
-      R"cpp(
+      {
+          R"cpp(
         struct Foo;
         #define MACRO Foo
       )cpp",
-      R"cpp(
+          R"cpp(
         struct $spelled[[Foo]] {
           $spelled[[Foo]]();
           ~$spelled[[Foo]]();
@@ -925,24 +1028,24 @@
         $spelled[[Foo]] Variable1;
         $implicit[[MACRO]] Variable2;
       )cpp",
-      "Foo",
-    },
-    {
-      R"cpp(
+          "Foo",
+      },
+      {
+          R"cpp(
         class Foo {
         public:
           Foo() = default;
         };
       )cpp",
-      R"cpp(
+          R"cpp(
         void f() { Foo $implicit[[f]]; f = $spelled[[Foo]]();}
       )cpp",
-      "Foo::Foo" /// constructor.
-    },
+          "Foo::Foo" /// constructor.
+      },
   };
   CollectorOpts.RefFilter = RefKind::All;
   CollectorOpts.RefsInHeaders = false;
-  for (const auto& T : TestCases) {
+  for (const auto &T : TestCases) {
     Annotations Header(T.Header);
     Annotations Main(T.Main);
     // Reset the file system.
Index: clang-tools-extra/clangd/index/SymbolCollector.cpp
===================================================================
--- clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -193,6 +193,8 @@
   llvm::DenseMap<const FileEntry *, const std::string *> CacheFEToURI;
   llvm::StringMap<std::string> CachePathToURI;
   llvm::DenseMap<FileID, llvm::StringRef> CacheFIDToInclude;
+  llvm::StringMap<std::string> CachePathToFrameworkSpelling;
+  llvm::StringMap<std::string> CacheFrameworkToUmbrellaInclude;
 
 public:
   HeaderFileURICache(Preprocessor *&PP, const SourceManager &SM,
@@ -249,6 +251,87 @@
     return R.first->second;
   }
 
+  struct FrameworkInfo {
+    // Path to the Headers dir e.g. /Frameworks/Foundation.framework/Headers/
+    llvm::StringRef HeadersDir;
+    // Subpath relative to the Headers dir, e.g. NSObject.h
+    llvm::StringRef HeaderSubpath;
+
+    bool valid() { return !HeadersDir.empty() && !HeaderSubpath.empty(); }
+  };
+
+  FrameworkInfo getHeaderFrameworkInfo(llvm::StringRef Path) {
+    using namespace llvm::sys;
+    path::reverse_iterator I = path::rbegin(Path);
+    path::reverse_iterator Prev = I;
+    path::reverse_iterator E = path::rend(Path);
+    while (I != E) {
+      if (*I == "Headers") {
+        auto PrefixLen = Prev - E;
+        return {Path.substr(0, PrefixLen), Path.substr(PrefixLen)};
+      }
+      // We don't currently support handling the private headers dir, just
+      // return early.
+      if (*I == "PrivateHeaders") {
+        return {StringRef(), StringRef()};
+      }
+      Prev = I;
+      ++I;
+    }
+    // Unexpected, must not be a framework header.
+    return {StringRef(), StringRef()};
+  }
+
+  llvm::StringRef getFrameworkIncludeSpelling(llvm::StringRef Path,
+                                              const FileEntry *FE,
+                                              llvm::StringRef Framework,
+                                              HeaderSearch &HS) {
+    auto SpellingR = CachePathToFrameworkSpelling.try_emplace(Path);
+    if (!SpellingR.second)
+      return SpellingR.first->second;
+
+    auto R = CacheFrameworkToUmbrellaInclude.try_emplace(Framework);
+    if (!R.second && !R.first->second.empty()) {
+      // Framework has known umbrella spelling, cache it for this header as
+      // well.
+      SpellingR.first->second = R.first->second;
+      return R.first->second;
+    }
+    FrameworkInfo Info = getHeaderFrameworkInfo(Path);
+    if (!Info.valid()) {
+      SpellingR.first->second = "";
+      R.first->second = "";
+      return R.first->second;
+    }
+    bool IsSystem = isSystem(HS.getFileDirFlavor(FE));
+
+    SmallString<256> UmbrellaPath(Info.HeadersDir);
+    llvm::sys::path::append(UmbrellaPath, Framework + ".h");
+
+    llvm::vfs::Status Status;
+    auto StatErr = HS.getFileMgr().getNoncachedStatValue(UmbrellaPath, Status);
+    if (StatErr) {
+      // Umbrella header does not exist, cache the failure.
+      R.first->second = "";
+      if (IsSystem) {
+        SpellingR.first->second =
+            llvm::formatv("<{0}/{1}>", Framework, Info.HeaderSubpath).str();
+      } else {
+        SpellingR.first->second =
+            llvm::formatv("\"{0}/{1}\"", Framework, Info.HeaderSubpath).str();
+      }
+      return SpellingR.first->second;
+    } else {
+      if (IsSystem) {
+        R.first->second = llvm::formatv("<{0}/{0}.h>", Framework);
+      } else {
+        R.first->second = llvm::formatv("\"{0}/{0}.h\"", Framework);
+      }
+      SpellingR.first->second = R.first->second;
+      return R.first->second;
+    }
+  }
+
   llvm::StringRef getIncludeHeaderUncached(FileID FID) {
     const FileEntry *FE = SM.getFileEntryForID(FID);
     if (!FE || FE->getName().empty())
@@ -265,6 +348,20 @@
         return toURI(Canonical);
       }
     }
+    // Special case framework headers since they have special framework-style
+    // include spelling. We also check if an umbrella header with the same name
+    // exists, and if so, use that instead.
+    auto &HS = PP->getHeaderSearchInfo();
+    if (const auto *HFI = HS.getExistingFileInfo(FE, /*WantExternal*/ false)) {
+      auto Framework = HFI->Framework;
+      if (!Framework.empty()) {
+        auto Spelling =
+            getFrameworkIncludeSpelling(Filename, FE, Framework, HS);
+        if (!Spelling.empty()) {
+          return Spelling;
+        }
+      }
+    }
     if (!isSelfContainedHeader(FE, FID, PP->getSourceManager(),
                                PP->getHeaderSearchInfo())) {
       // A .inc or .def file is often included into a real header to define
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to