Index: lib/Sema/SemaCodeComplete.cpp
===================================================================
--- lib/Sema/SemaCodeComplete.cpp
+++ lib/Sema/SemaCodeComplete.cpp
@@ -8371,10 +8371,23 @@
};
// Helper: scans IncludeDir for nice files, and adds results for each.
- auto AddFilesFromIncludeDir = [&](StringRef IncludeDir, bool IsSystem) {
+ auto AddFilesFromIncludeDir = [&](StringRef IncludeDir,
+ bool IsSystem,
+ DirectoryLookup::LookupType_t LookupType) {
llvm::SmallString<128> Dir = IncludeDir;
- if (!NativeRelDir.empty())
- llvm::sys::path::append(Dir, NativeRelDir);
+ if (!NativeRelDir.empty()) {
+ if (LookupType == DirectoryLookup::LT_Framework) {
+ // For a framework dir, #include actually maps to
+ // a path of Foo.framework/Headers/Bar/.
+ auto Begin = llvm::sys::path::begin(NativeRelDir);
+ auto End = llvm::sys::path::end(NativeRelDir);
+
+ llvm::sys::path::append(Dir, *Begin + ".framework", "Headers");
+ llvm::sys::path::append(Dir, ++Begin, End);
+ } else {
+ llvm::sys::path::append(Dir, NativeRelDir);
+ }
+ }
std::error_code EC;
unsigned Count = 0;
@@ -8385,6 +8398,12 @@
StringRef Filename = llvm::sys::path::filename(It->path());
switch (It->type()) {
case llvm::sys::fs::file_type::directory_file:
+ // All entries in a framework directory must have a ".framework" suffix,
+ // but the suffix does not appear in the source code's include/import.
+ if (LookupType == DirectoryLookup::LT_Framework &&
+ NativeRelDir.empty() && !Filename.consume_back(".framework"))
+ break;
+
AddCompletion(Filename, /*IsDirectory=*/true);
break;
case llvm::sys::fs::file_type::regular_file:
@@ -8413,10 +8432,12 @@
// header maps are not (currently) enumerable.
break;
case DirectoryLookup::LT_NormalDir:
- AddFilesFromIncludeDir(IncludeDir.getDir()->getName(), IsSystem);
+ AddFilesFromIncludeDir(IncludeDir.getDir()->getName(), IsSystem,
+ DirectoryLookup::LT_NormalDir);
break;
case DirectoryLookup::LT_Framework:
- AddFilesFromIncludeDir(IncludeDir.getFrameworkDir()->getName(), IsSystem);
+ AddFilesFromIncludeDir(IncludeDir.getFrameworkDir()->getName(), IsSystem,
+ DirectoryLookup::LT_Framework);
break;
}
};
@@ -8430,7 +8451,8 @@
// The current directory is on the include path for "quoted" includes.
auto *CurFile = PP.getCurrentFileLexer()->getFileEntry();
if (CurFile && CurFile->getDir())
- AddFilesFromIncludeDir(CurFile->getDir()->getName(), false);
+ AddFilesFromIncludeDir(CurFile->getDir()->getName(), false,
+ DirectoryLookup::LT_NormalDir);
for (const auto &D : make_range(S.quoted_dir_begin(), S.quoted_dir_end()))
AddFilesFromDirLookup(D, false);
}
Index: test/CodeCompletion/included-frameworks.m
===================================================================
--- /dev/null
+++ test/CodeCompletion/included-frameworks.m
@@ -0,0 +1,29 @@
+// RUN: rm -rf %t && mkdir -p %t/Foo.framework/Headers/SubFolder && mkdir %t/NotAFramework/
+// RUN: touch %t/Foo.framework/Headers/Foo.h && touch %t/Foo.framework/Headers/FOOClass.h
+// RUN: touch %t/Foo.framework/Headers/SubFolder/FOOInternal.h
+
+#import
+
+#import
+
+// Note: the run lines follow their respective tests, since line/column
+// matter in this test.
+
+// Autocomplete frameworks without the ".framework" extension.
+//
+// RUN: %clang -fsyntax-only -F %t -Xclang -code-completion-at=%s:5:10 %s -o - | FileCheck -check-prefix=CHECK-1 %s
+// CHECK-1-NOT: Foo.framework/
+// CHECK-1-NOT: NotAFramework/
+// CHECK-1: Foo/
+
+// Autocomplete for frameworks inside its Headers folder.
+//
+// RUN: %clang -fsyntax-only -F %t -Xclang -code-completion-at=%s:5:14 %s -o - | FileCheck -check-prefix=CHECK-2 %s
+// CHECK-2: Foo.h>
+// CHECK-2: FOOClass.h>
+// CHECK-2: SubFolder/
+
+// Autocomplete for folders inside of a frameworks.
+//
+// RUN: %clang -fsyntax-only -F %t -Xclang -code-completion-at=%s:7:24 %s -o - | FileCheck -check-prefix=CHECK-3 %s
+// CHECK-3: FOOInternal.h>