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>