diff --git a/clang-tools-extra/clangd/Headers.h b/clang-tools-extra/clangd/Headers.h --- a/clang-tools-extra/clangd/Headers.h +++ b/clang-tools-extra/clangd/Headers.h @@ -25,6 +25,8 @@ #include "clang/Tooling/Inclusions/StandardLibrary.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" #include "llvm/Support/Error.h" @@ -139,6 +141,9 @@ llvm::Optional getID(const FileEntry *Entry) const; HeaderID getOrCreateID(const FileEntry *Entry); + llvm::Optional + getRealNameFromSuffix(llvm::StringRef Suffix) const; + StringRef getRealPath(HeaderID ID) const { assert(static_cast(ID) <= RealPathNames.size()); return RealPathNames[static_cast(ID)]; @@ -188,6 +193,7 @@ // Contains HeaderIDs of all non self-contained entries in the // IncludeStructure. llvm::DenseSet NonSelfContained; + llvm::StringMap SuffixToRealPath; }; // Calculates insertion edit for including a new header in a file. diff --git a/clang-tools-extra/clangd/Headers.cpp b/clang-tools-extra/clangd/Headers.cpp --- a/clang-tools-extra/clangd/Headers.cpp +++ b/clang-tools-extra/clangd/Headers.cpp @@ -19,6 +19,7 @@ #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/PPCallbacks.h" #include "clang/Lex/Preprocessor.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Path.h" @@ -229,6 +230,8 @@ return It->second; } +const static int MaxComponents = 4; + IncludeStructure::HeaderID IncludeStructure::getOrCreateID(const FileEntry *Entry) { // Main file's FileEntry was not known at IncludeStructure creation time. @@ -244,11 +247,34 @@ RealPathNames.emplace_back(); IncludeStructure::HeaderID Result = R.first->getSecond(); std::string &RealPathName = RealPathNames[static_cast(Result)]; - if (RealPathName.empty()) - RealPathName = Entry->tryGetRealPathName().str(); + if (RealPathName.empty()) { + auto RealPath = Entry->tryGetRealPathName().str(); + if (!RealPath.empty()) { + RealPathName = RealPath; + + int Components = 1; + + // FIXME: check that this works on Windows and add tests. + for (auto It = llvm::sys::path::rbegin(RealPath), + End = llvm::sys::path::rend(RealPath); + It != End && Components <= MaxComponents; ++It, ++Components) { + auto Suffix = RealPath.substr(It->data() - RealPath.data()); + vlog("Mapping header {0} suffix {1}", RealPath, Suffix); + SuffixToRealPath[Suffix] = RealPath; + } + } + } return Result; } +llvm::Optional +IncludeStructure::getRealNameFromSuffix(llvm::StringRef Suffix) const { + auto It = SuffixToRealPath.find(Suffix); + if (It == SuffixToRealPath.end()) + return llvm::None; + return It->second; +} + llvm::DenseMap IncludeStructure::includeDepth(HeaderID Root) const { // Include depth 0 is the main file only. diff --git a/clang-tools-extra/clangd/IncludeCleaner.h b/clang-tools-extra/clangd/IncludeCleaner.h --- a/clang-tools-extra/clangd/IncludeCleaner.h +++ b/clang-tools-extra/clangd/IncludeCleaner.h @@ -13,9 +13,6 @@ /// to provide useful warnings in most popular scenarios but not 1:1 exact /// feature compatibility. /// -/// FIXME(kirillbobyrev): Add support for IWYU pragmas. -/// FIXME(kirillbobyrev): Add support for standard library headers. -/// //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_INCLUDECLEANER_H @@ -23,6 +20,7 @@ #include "Headers.h" #include "ParsedAST.h" +#include "index/CanonicalIncludes.h" #include "clang/Basic/SourceLocation.h" #include "clang/Tooling/Inclusions/StandardLibrary.h" #include "llvm/ADT/DenseSet.h" @@ -72,6 +70,7 @@ llvm::function_ref HeaderResponsible); ReferencedFiles findReferencedFiles(const ReferencedLocations &Locs, const IncludeStructure &Includes, + const CanonicalIncludes &CanonIncludes, const SourceManager &SM); /// Maps FileIDs to the internal IncludeStructure representation (HeaderIDs). diff --git a/clang-tools-extra/clangd/IncludeCleaner.cpp b/clang-tools-extra/clangd/IncludeCleaner.cpp --- a/clang-tools-extra/clangd/IncludeCleaner.cpp +++ b/clang-tools-extra/clangd/IncludeCleaner.cpp @@ -12,6 +12,7 @@ #include "ParsedAST.h" #include "Protocol.h" #include "SourceCode.h" +#include "index/CanonicalIncludes.h" #include "support/Logger.h" #include "support/Trace.h" #include "clang/AST/ASTContext.h" @@ -266,7 +267,8 @@ // include, so it will be responsible for bringing the symbols from given // header into the scope. FileID headerResponsible(FileID ID, const SourceManager &SM, - const IncludeStructure &Includes) { + const IncludeStructure &Includes, + const CanonicalIncludes &CanonIncludes) { // Unroll the chain of non self-contained headers until we find the one that // can be included. for (const FileEntry *FE = SM.getFileEntryForID(ID); ID != SM.getMainFileID(); @@ -277,11 +279,28 @@ auto HID = Includes.getID(FE); assert(HID && "We're iterating over headers already existing in " "IncludeStructure"); - if (Includes.isSelfContained(*HID)) - break; - // The header is not self-contained: put the responsibility for its symbols - // on its includer. - ID = SM.getFileID(SM.getIncludeLoc(ID)); + if (!Includes.isSelfContained(*HID)) { + // The header is not self-contained: put the responsibility for its + // symbols on its includer. + ID = SM.getFileID(SM.getIncludeLoc(ID)); + continue; + } + if (auto Filename = SM.getNonBuiltinFilenameForID(ID)) { + vlog("Querying canonical include for {0}", *Filename); + auto CanonicalHeader = CanonIncludes.mapHeader(*Filename); + if (CanonicalHeader.empty()) + break; + CanonicalHeader = CanonicalHeader.drop_front().drop_back(); + auto FullHeader = Includes.getRealNameFromSuffix(CanonicalHeader); + vlog("Canoncal include for {0}: {1}, full: {2}", *Filename, + CanonicalHeader, FullHeader); + if (!FullHeader.hasValue()) + break; + auto Entry = SM.getFileManager().getFile(*FullHeader); + assert(Entry); + ID = SM.translateFile(*Entry); + } + break; } return ID; } @@ -339,10 +358,12 @@ ReferencedFiles findReferencedFiles(const ReferencedLocations &Locs, const IncludeStructure &Includes, + const CanonicalIncludes &CanonIncludes, const SourceManager &SM) { - return findReferencedFiles(Locs, SM, [&SM, &Includes](FileID ID) { - return headerResponsible(ID, SM, Includes); - }); + return findReferencedFiles( + Locs, SM, [&SM, &Includes, &CanonIncludes](FileID ID) { + return headerResponsible(ID, SM, Includes, CanonIncludes); + }); } std::vector @@ -402,8 +423,9 @@ const auto &SM = AST.getSourceManager(); auto Refs = findReferencedLocations(AST); - auto ReferencedFileIDs = findReferencedFiles(Refs, AST.getIncludeStructure(), - AST.getSourceManager()); + auto ReferencedFileIDs = + findReferencedFiles(Refs, AST.getIncludeStructure(), + AST.getCanonicalIncludes(), AST.getSourceManager()); auto ReferencedHeaders = translateToHeaderIDs(ReferencedFileIDs, AST.getIncludeStructure(), SM); return getUnused(AST, ReferencedHeaders); diff --git a/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp b/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp --- a/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp +++ b/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp @@ -266,8 +266,9 @@ ReferencedLocations Locs = findReferencedLocations(AST); EXPECT_THAT(Locs.Stdlib, ElementsAreArray(WantSyms)); - ReferencedFiles Files = findReferencedFiles(Locs, AST.getIncludeStructure(), - AST.getSourceManager()); + ReferencedFiles Files = + findReferencedFiles(Locs, AST.getIncludeStructure(), + AST.getCanonicalIncludes(), AST.getSourceManager()); EXPECT_THAT(Files.Stdlib, ElementsAreArray(WantHeaders)); } } @@ -378,8 +379,8 @@ auto &SM = AST.getSourceManager(); auto &Includes = AST.getIncludeStructure(); - auto ReferencedFiles = - findReferencedFiles(findReferencedLocations(AST), Includes, SM); + auto ReferencedFiles = findReferencedFiles( + findReferencedLocations(AST), Includes, AST.getCanonicalIncludes(), SM); llvm::StringSet<> ReferencedFileNames; for (FileID FID : ReferencedFiles.User) ReferencedFileNames.insert( @@ -427,9 +428,9 @@ ParsedAST AST = TU.build(); - auto ReferencedFiles = - findReferencedFiles(findReferencedLocations(AST), - AST.getIncludeStructure(), AST.getSourceManager()); + auto ReferencedFiles = findReferencedFiles( + findReferencedLocations(AST), AST.getIncludeStructure(), + AST.getCanonicalIncludes(), AST.getSourceManager()); llvm::StringSet<> ReferencedFileNames; auto &SM = AST.getSourceManager(); for (FileID FID : ReferencedFiles.User) @@ -461,9 +462,9 @@ ParsedAST AST = TU.build(); - auto ReferencedFiles = - findReferencedFiles(findReferencedLocations(AST), - AST.getIncludeStructure(), AST.getSourceManager()); + auto ReferencedFiles = findReferencedFiles( + findReferencedLocations(AST), AST.getIncludeStructure(), + AST.getCanonicalIncludes(), AST.getSourceManager()); llvm::StringSet<> ReferencedFileNames; auto &SM = AST.getSourceManager(); for (FileID FID : ReferencedFiles.User) @@ -483,9 +484,9 @@ TU.AdditionalFiles["behind_keep.h"] = guard(""); ParsedAST AST = TU.build(); - auto ReferencedFiles = - findReferencedFiles(findReferencedLocations(AST), - AST.getIncludeStructure(), AST.getSourceManager()); + auto ReferencedFiles = findReferencedFiles( + findReferencedLocations(AST), AST.getIncludeStructure(), + AST.getCanonicalIncludes(), AST.getSourceManager()); EXPECT_TRUE(ReferencedFiles.User.empty()); EXPECT_THAT(AST.getDiagnostics(), llvm::ValueIs(IsEmpty())); }