From 3e804d25819877bbc852860a2cc743fcb76a71c7 Mon Sep 17 00:00:00 2001 From: David Goldman Date: Wed, 27 Feb 2019 17:40:33 +0000 Subject: [PATCH] Support framework import/include auto-completion Frameworks filesystem representations: UIKit.framework/Headers/%header% Framework import format: #import Thus the completion code must map the input format of to the path of UIKit.framework/Headers as well as strip the ".framework" suffix when auto-completing the framework name. llvm-svn: 355008 --- clang/lib/Sema/SemaCodeComplete.cpp | 34 +++++++++++++++---- .../test/CodeCompletion/included-frameworks.m | 29 ++++++++++++++++ 2 files changed, 57 insertions(+), 6 deletions(-) create mode 100644 clang/test/CodeCompletion/included-frameworks.m diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp index a727bad97c13..08b61e198700 100644 --- a/clang/lib/Sema/SemaCodeComplete.cpp +++ b/clang/lib/Sema/SemaCodeComplete.cpp @@ -8404,10 +8404,23 @@ void Sema::CodeCompleteIncludedFile(llvm::StringRef Dir, bool Angled) { }; // 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; @@ -8418,6 +8431,12 @@ void Sema::CodeCompleteIncludedFile(llvm::StringRef Dir, bool Angled) { 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: @@ -8446,10 +8465,12 @@ void Sema::CodeCompleteIncludedFile(llvm::StringRef Dir, bool Angled) { // 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; } }; @@ -8463,7 +8484,8 @@ void Sema::CodeCompleteIncludedFile(llvm::StringRef Dir, bool Angled) { // 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); } diff --git a/clang/test/CodeCompletion/included-frameworks.m b/clang/test/CodeCompletion/included-frameworks.m new file mode 100644 index 000000000000..737a360a8f93 --- /dev/null +++ b/clang/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>