diff --git a/clang/include/clang/Lex/HeaderMap.h b/clang/include/clang/Lex/HeaderMap.h --- a/clang/include/clang/Lex/HeaderMap.h +++ b/clang/include/clang/Lex/HeaderMap.h @@ -16,6 +16,7 @@ #include "clang/Basic/FileManager.h" #include "clang/Basic/LLVM.h" #include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringMap.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/MemoryBuffer.h" #include @@ -29,6 +30,7 @@ class HeaderMapImpl { std::unique_ptr FileBuffer; bool NeedsBSwap; + mutable llvm::StringMap ReverseMap; public: HeaderMapImpl(std::unique_ptr File, bool NeedsBSwap) @@ -48,6 +50,9 @@ /// Print the contents of this headermap to stderr. void dump() const; + /// Return key for specifed path. + StringRef reverseLookupFilename(StringRef DestPath) const; + private: unsigned getEndianAdjustedWord(unsigned X) const; const HMapHeader &getHeader() const; @@ -79,9 +84,10 @@ /// ".." and a filename "../file.h" this would be "../../file.h". Optional LookupFile(StringRef Filename, FileManager &FM) const; - using HeaderMapImpl::lookupFilename; - using HeaderMapImpl::getFileName; using HeaderMapImpl::dump; + using HeaderMapImpl::getFileName; + using HeaderMapImpl::lookupFilename; + using HeaderMapImpl::reverseLookupFilename; }; } // end namespace clang. diff --git a/clang/lib/Lex/HeaderMap.cpp b/clang/lib/Lex/HeaderMap.cpp --- a/clang/lib/Lex/HeaderMap.cpp +++ b/clang/lib/Lex/HeaderMap.cpp @@ -240,3 +240,32 @@ return StringRef(DestPath.begin(), DestPath.size()); } } + +StringRef HeaderMapImpl::reverseLookupFilename(StringRef DestPath) const { + if (!ReverseMap.empty()) + return ReverseMap.lookup(DestPath); + + const HMapHeader &Hdr = getHeader(); + unsigned NumBuckets = getEndianAdjustedWord(Hdr.NumBuckets); + StringRef RetKey; + for (unsigned i = 0; i != NumBuckets; ++i) { + HMapBucket B = getBucket(i); + if (B.Key == HMAP_EmptyBucketKey) + continue; + + Optional Key = getString(B.Key); + Optional Prefix = getString(B.Prefix); + Optional Suffix = getString(B.Suffix); + if (LLVM_LIKELY(Key && Prefix && Suffix)) { + SmallVector Buf; + Buf.append(Prefix->begin(), Prefix->end()); + Buf.append(Suffix->begin(), Suffix->end()); + StringRef Value(Buf.begin(), Buf.size()); + ReverseMap[Value] = *Key; + + if (DestPath == Value) + RetKey = *Key; + } + } + return RetKey; +} diff --git a/clang/lib/Lex/HeaderSearch.cpp b/clang/lib/Lex/HeaderSearch.cpp --- a/clang/lib/Lex/HeaderSearch.cpp +++ b/clang/lib/Lex/HeaderSearch.cpp @@ -1834,7 +1834,7 @@ }; for (unsigned I = 0; I != SearchDirs.size(); ++I) { - // FIXME: Support this search within frameworks and header maps. + // FIXME: Support this search within frameworks. if (!SearchDirs[I].isNormalDir()) continue; @@ -1848,6 +1848,19 @@ if (!BestPrefixLength && CheckDir(path::parent_path(MainFile)) && IsSystem) *IsSystem = false; + // Try resolving resulting filename via reverse search in header maps, + // key from header name is user prefered name for the include file. + StringRef Filename = File.drop_front(BestPrefixLength); + for (unsigned I = 0; I != SearchDirs.size(); ++I) { + if (!SearchDirs[I].isHeaderMap()) + continue; - return path::convert_to_slash(File.drop_front(BestPrefixLength)); + StringRef SpelledFilename = + SearchDirs[I].getHeaderMap()->reverseLookupFilename(Filename); + if (!SpelledFilename.empty()) { + Filename = SpelledFilename; + break; + } + } + return path::convert_to_slash(Filename); } diff --git a/clang/unittests/Lex/HeaderSearchTest.cpp b/clang/unittests/Lex/HeaderSearchTest.cpp --- a/clang/unittests/Lex/HeaderSearchTest.cpp +++ b/clang/unittests/Lex/HeaderSearchTest.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "clang/Lex/HeaderSearch.h" +#include "HeaderMapTestUtils.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/FileManager.h" @@ -45,6 +46,21 @@ Search.AddSearchPath(DL, /*isAngled=*/false); } + void addHeaderMap(llvm::StringRef Filename, + std::unique_ptr Buf) { + VFS->addFile(Filename, 0, std::move(Buf), /*User=*/None, /*Group=*/None, + llvm::sys::fs::file_type::regular_file); + auto FE = FileMgr.getFile(Filename, true); + assert(FE); + + // Test class supports only one HMap at a time. + assert(!HMap); + HMap = HeaderMap::Create(*FE, FileMgr); + auto DL = + DirectoryLookup(HMap.get(), SrcMgr::C_User, /*isFramework=*/false); + Search.AddSearchPath(DL, /*isAngled=*/false); + } + IntrusiveRefCntPtr VFS; FileSystemOptions FileMgrOpts; FileManager FileMgr; @@ -55,6 +71,7 @@ std::shared_ptr TargetOpts; IntrusiveRefCntPtr Target; HeaderSearch Search; + std::unique_ptr HMap; }; TEST_F(HeaderSearchTest, NoSearchDir) { @@ -136,5 +153,31 @@ "y/z/t.h"); } +// Helper struct with null terminator character to make MemoryBuffer happy. +template +struct NullTerminatedFile : public FileTy { + PaddingTy Padding = 0; +}; + +TEST_F(HeaderSearchTest, HeaderMapReverseLookup) { + typedef NullTerminatedFile, char> FileTy; + FileTy File; + File.init(); + + test::HMapFileMockMaker Maker(File); + auto a = Maker.addString("d.h"); + auto b = Maker.addString("b/"); + auto c = Maker.addString("c.h"); + Maker.addBucket("d.h", a, b, c); + + addHeaderMap("/x/y/z.hmap", File.getBuffer()); + addSearchDir("/a"); + + EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/a/b/c.h", + /*WorkingDir=*/"", + /*MainFile=*/""), + "d.h"); +} + } // namespace } // namespace clang