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" 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" 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,25 @@ 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)) { + auto CanonicalHeaderSuffix = CanonIncludes.mapHeader(*Filename); + if (CanonicalHeaderSuffix.empty()) + break; + CanonicalHeaderSuffix = CanonicalHeaderSuffix.drop_front().drop_back(); + auto FullHeader = CanonIncludes.suffixToHeader(CanonicalHeaderSuffix); + if (FullHeader.empty()) + break; + auto Entry = SM.getFileManager().getFile(FullHeader); + assert(Entry); + ID = SM.translateFile(*Entry); + } + break; } return ID; } @@ -339,10 +355,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 +420,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/ParsedAST.cpp b/clang-tools-extra/clangd/ParsedAST.cpp --- a/clang-tools-extra/clangd/ParsedAST.cpp +++ b/clang-tools-extra/clangd/ParsedAST.cpp @@ -544,9 +544,10 @@ CanonIncludes = Preamble->CanonIncludes; else CanonIncludes.addSystemHeadersMapping(Clang->getLangOpts()); - std::unique_ptr IWYUHandler = - collectIWYUHeaderMaps(&CanonIncludes); + auto IWYUHandler = collectIWYUHeaderMaps(&CanonIncludes); Clang->getPreprocessor().addCommentHandler(IWYUHandler.get()); + auto SuffixMappingCollector = collectSuffixMappings(&CanonIncludes); + Clang->getPreprocessor().addPPCallbacks(move(SuffixMappingCollector)); // Collect tokens of the main file. syntax::TokenCollector CollectTokens(Clang->getPreprocessor()); diff --git a/clang-tools-extra/clangd/Preamble.cpp b/clang-tools-extra/clangd/Preamble.cpp --- a/clang-tools-extra/clangd/Preamble.cpp +++ b/clang-tools-extra/clangd/Preamble.cpp @@ -11,6 +11,7 @@ #include "Config.h" #include "Headers.h" #include "SourceCode.h" +#include "index/CanonicalIncludes.h" #include "support/Logger.h" #include "support/ThreadsafeFS.h" #include "support/Trace.h" @@ -107,7 +108,9 @@ return std::make_unique( std::make_unique(*SourceMgr, Macros), - collectPragmaMarksCallback(*SourceMgr, Marks)); + std::make_unique( + collectPragmaMarksCallback(*SourceMgr, Marks), + collectSuffixMappings(&CanonIncludes))); } CommentHandler *getCommentHandler() override { diff --git a/clang-tools-extra/clangd/index/CanonicalIncludes.h b/clang-tools-extra/clangd/index/CanonicalIncludes.h --- a/clang-tools-extra/clangd/index/CanonicalIncludes.h +++ b/clang-tools-extra/clangd/index/CanonicalIncludes.h @@ -19,6 +19,7 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_CANONICALINCLUDES_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_CANONICALINCLUDES_H +#include "clang/Lex/PPCallbacks.h" #include "clang/Lex/Preprocessor.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" @@ -54,9 +55,14 @@ /// headers. void addSystemHeadersMapping(const LangOptions &Language); + void addHeader(llvm::StringRef Header); + + llvm::StringRef suffixToHeader(llvm::StringRef Suffix) const; + private: /// A map from full include path to a canonical path. llvm::StringMap FullPathMapping; + llvm::StringMap SuffixToFullPathMapping; /// A map from a suffix (one or components of a path) to a canonical path. /// Used only for mapping standard headers. const llvm::StringMap *StdSuffixHeaderMapping = nullptr; @@ -89,6 +95,8 @@ std::unique_ptr collectIWYUHeaderMaps(CanonicalIncludes *Includes); +std::unique_ptr collectSuffixMappings(CanonicalIncludes *Includes); + } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/index/CanonicalIncludes.cpp b/clang-tools-extra/clangd/index/CanonicalIncludes.cpp --- a/clang-tools-extra/clangd/index/CanonicalIncludes.cpp +++ b/clang-tools-extra/clangd/index/CanonicalIncludes.cpp @@ -9,6 +9,7 @@ #include "CanonicalIncludes.h" #include "Headers.h" #include "clang/Driver/Types.h" +#include "clang/Lex/PPCallbacks.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Path.h" #include @@ -55,9 +56,26 @@ return StdSymbolMapping ? StdSymbolMapping->lookup(QName) : ""; } +void CanonicalIncludes::addHeader(llvm::StringRef Header) { + for (auto It = llvm::sys::path::rbegin(Header), + End = llvm::sys::path::rend(Header); + It != End; ++It, ++Components) { + auto Suffix = Header.substr(It->data() - Header.begin()); + SuffixToFullPathMapping[Suffix] = Header.str(); + } +} + +llvm::StringRef +CanonicalIncludes::suffixToHeader(llvm::StringRef Suffix) const { + auto It = SuffixToFullPathMapping.find(Suffix); + if (It == SuffixToFullPathMapping.end()) + return StringRef(); + return It->second; +} + std::unique_ptr collectIWYUHeaderMaps(CanonicalIncludes *Includes) { - class PragmaCommentHandler : public clang::CommentHandler { + class PragmaCommentHandler : public CommentHandler { public: PragmaCommentHandler(CanonicalIncludes *Includes) : Includes(Includes) {} @@ -81,6 +99,29 @@ return std::make_unique(Includes); } +std::unique_ptr +collectSuffixMappings(CanonicalIncludes *Includes) { + class SuffixMappingCollector : public PPCallbacks { + public: + SuffixMappingCollector(CanonicalIncludes *Includes) : Includes(Includes) {} + + void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, + llvm::StringRef FileName, bool IsAngled, + CharSourceRange /*FilenameRange*/, + const FileEntry *File, + llvm::StringRef /*SearchPath*/, + llvm::StringRef /*RelativePath*/, + const clang::Module * /*Imported*/, + SrcMgr::CharacteristicKind FileKind) override { + if (File) + Includes->addHeader(File->getName()); + } + + private: + CanonicalIncludes *const Includes; + }; + return std::make_unique(Includes); +} void CanonicalIncludes::addSystemHeadersMapping(const LangOptions &Language) { if (Language.CPlusPlus) { static const auto *Symbols = new llvm::StringMap({ 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())); }