diff options
Diffstat (limited to 'contrib/llvm-project/clang/lib/Frontend/HeaderIncludeGen.cpp')
-rw-r--r-- | contrib/llvm-project/clang/lib/Frontend/HeaderIncludeGen.cpp | 186 |
1 files changed, 154 insertions, 32 deletions
diff --git a/contrib/llvm-project/clang/lib/Frontend/HeaderIncludeGen.cpp b/contrib/llvm-project/clang/lib/Frontend/HeaderIncludeGen.cpp index 1ee47d8d2480..992c2670260e 100644 --- a/contrib/llvm-project/clang/lib/Frontend/HeaderIncludeGen.cpp +++ b/contrib/llvm-project/clang/lib/Frontend/HeaderIncludeGen.cpp @@ -12,6 +12,7 @@ #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Lex/Preprocessor.h" #include "llvm/ADT/SmallString.h" +#include "llvm/Support/JSON.h" #include "llvm/Support/raw_ostream.h" using namespace clang; @@ -42,6 +43,62 @@ public: delete OutputFile; } + HeaderIncludesCallback(const HeaderIncludesCallback &) = delete; + HeaderIncludesCallback &operator=(const HeaderIncludesCallback &) = delete; + + void FileChanged(SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, + FileID PrevFID) override; + + void FileSkipped(const FileEntryRef &SkippedFile, const Token &FilenameTok, + SrcMgr::CharacteristicKind FileType) override; + +private: + bool ShouldShowHeader(SrcMgr::CharacteristicKind HeaderType) { + if (!DepOpts.IncludeSystemHeaders && isSystem(HeaderType)) + return false; + + // Show the current header if we are (a) past the predefines, or (b) showing + // all headers and in the predefines at a depth past the initial file and + // command line buffers. + return (HasProcessedPredefines || + (ShowAllHeaders && CurrentIncludeDepth > 2)); + } +}; + +/// A callback for emitting header usage information to a file in JSON. Each +/// line in the file is a JSON object that includes the source file name and +/// the list of headers directly or indirectly included from it. For example: +/// +/// {"source":"/tmp/foo.c", +/// "includes":["/usr/include/stdio.h", "/usr/include/stdlib.h"]} +/// +/// To reduce the amount of data written to the file, we only record system +/// headers that are directly included from a file that isn't in the system +/// directory. +class HeaderIncludesJSONCallback : public PPCallbacks { + SourceManager &SM; + raw_ostream *OutputFile; + bool OwnsOutputFile; + SmallVector<std::string, 16> IncludedHeaders; + +public: + HeaderIncludesJSONCallback(const Preprocessor *PP, raw_ostream *OutputFile_, + bool OwnsOutputFile_) + : SM(PP->getSourceManager()), OutputFile(OutputFile_), + OwnsOutputFile(OwnsOutputFile_) {} + + ~HeaderIncludesJSONCallback() override { + if (OwnsOutputFile) + delete OutputFile; + } + + HeaderIncludesJSONCallback(const HeaderIncludesJSONCallback &) = delete; + HeaderIncludesJSONCallback & + operator=(const HeaderIncludesJSONCallback &) = delete; + + void EndOfMainFile() override; + void FileChanged(SourceLocation Loc, FileChangeReason Reason, SrcMgr::CharacteristicKind FileType, FileID PrevFID) override; @@ -116,16 +173,33 @@ void clang::AttachHeaderIncludeGen(Preprocessor &PP, } } - // Print header info for extra headers, pretending they were discovered by - // the regular preprocessor. The primary use case is to support proper - // generation of Make / Ninja file dependencies for implicit includes, such - // as sanitizer blacklists. It's only important for cl.exe compatibility, - // the GNU way to generate rules is -M / -MM / -MD / -MMD. - for (const auto &Header : DepOpts.ExtraDeps) - PrintHeaderInfo(OutputFile, Header.first, ShowDepth, 2, MSStyle); - PP.addPPCallbacks(std::make_unique<HeaderIncludesCallback>( - &PP, ShowAllHeaders, OutputFile, DepOpts, OwnsOutputFile, ShowDepth, - MSStyle)); + switch (DepOpts.HeaderIncludeFormat) { + case HIFMT_None: + llvm_unreachable("unexpected header format kind"); + case HIFMT_Textual: { + assert(DepOpts.HeaderIncludeFiltering == HIFIL_None && + "header filtering is currently always disabled when output format is" + "textual"); + // Print header info for extra headers, pretending they were discovered by + // the regular preprocessor. The primary use case is to support proper + // generation of Make / Ninja file dependencies for implicit includes, such + // as sanitizer ignorelists. It's only important for cl.exe compatibility, + // the GNU way to generate rules is -M / -MM / -MD / -MMD. + for (const auto &Header : DepOpts.ExtraDeps) + PrintHeaderInfo(OutputFile, Header.first, ShowDepth, 2, MSStyle); + PP.addPPCallbacks(std::make_unique<HeaderIncludesCallback>( + &PP, ShowAllHeaders, OutputFile, DepOpts, OwnsOutputFile, ShowDepth, + MSStyle)); + break; + } + case HIFMT_JSON: { + assert(DepOpts.HeaderIncludeFiltering == HIFIL_Only_Direct_System && + "only-direct-system is the only option for filtering"); + PP.addPPCallbacks(std::make_unique<HeaderIncludesJSONCallback>( + &PP, OutputFile, OwnsOutputFile)); + break; + } + } } void HeaderIncludesCallback::FileChanged(SourceLocation Loc, @@ -147,38 +221,24 @@ void HeaderIncludesCallback::FileChanged(SourceLocation Loc, // We track when we are done with the predefines by watching for the first // place where we drop back to a nesting depth of 1. - if (CurrentIncludeDepth == 1 && !HasProcessedPredefines) { - if (!DepOpts.ShowIncludesPretendHeader.empty()) { - PrintHeaderInfo(OutputFile, DepOpts.ShowIncludesPretendHeader, - ShowDepth, 2, MSStyle); - } + if (CurrentIncludeDepth == 1 && !HasProcessedPredefines) HasProcessedPredefines = true; - } return; - } else + } else { + return; + } + + if (!ShouldShowHeader(NewFileType)) return; - // Show the header if we are (a) past the predefines, or (b) showing all - // headers and in the predefines at a depth past the initial file and command - // line buffers. - bool ShowHeader = (HasProcessedPredefines || - (ShowAllHeaders && CurrentIncludeDepth > 2)); unsigned IncludeDepth = CurrentIncludeDepth; if (!HasProcessedPredefines) --IncludeDepth; // Ignore indent from <built-in>. - else if (!DepOpts.ShowIncludesPretendHeader.empty()) - ++IncludeDepth; // Pretend inclusion by ShowIncludesPretendHeader. - - if (!DepOpts.IncludeSystemHeaders && isSystem(NewFileType)) - ShowHeader = false; - // Dump the header include information we are past the predefines buffer or - // are showing all headers and this isn't the magic implicit <command line> - // header. // FIXME: Identify headers in a more robust way than comparing their name to // "<command line>" and "<built-in>" in a bunch of places. - if (ShowHeader && Reason == PPCallbacks::EnterFile && + if (Reason == PPCallbacks::EnterFile && UserLoc.getFilename() != StringRef("<command line>")) { PrintHeaderInfo(OutputFile, UserLoc.getFilename(), ShowDepth, IncludeDepth, MSStyle); @@ -191,9 +251,71 @@ void HeaderIncludesCallback::FileSkipped(const FileEntryRef &SkippedFile, const if (!DepOpts.ShowSkippedHeaderIncludes) return; - if (!DepOpts.IncludeSystemHeaders && isSystem(FileType)) + if (!ShouldShowHeader(FileType)) return; PrintHeaderInfo(OutputFile, SkippedFile.getName(), ShowDepth, CurrentIncludeDepth + 1, MSStyle); } + +void HeaderIncludesJSONCallback::EndOfMainFile() { + OptionalFileEntryRef FE = SM.getFileEntryRefForID(SM.getMainFileID()); + SmallString<256> MainFile(FE->getName()); + SM.getFileManager().makeAbsolutePath(MainFile); + + std::string Str; + llvm::raw_string_ostream OS(Str); + llvm::json::OStream JOS(OS); + JOS.object([&] { + JOS.attribute("source", MainFile.c_str()); + JOS.attributeArray("includes", [&] { + llvm::StringSet<> SeenHeaders; + for (const std::string &H : IncludedHeaders) + if (SeenHeaders.insert(H).second) + JOS.value(H); + }); + }); + OS << "\n"; + + if (OutputFile->get_kind() == raw_ostream::OStreamKind::OK_FDStream) { + llvm::raw_fd_ostream *FDS = static_cast<llvm::raw_fd_ostream *>(OutputFile); + if (auto L = FDS->lock()) + *OutputFile << Str; + } else + *OutputFile << Str; +} + +/// Determine whether the header file should be recorded. The header file should +/// be recorded only if the header file is a system header and the current file +/// isn't a system header. +static bool shouldRecordNewFile(SrcMgr::CharacteristicKind NewFileType, + SourceLocation PrevLoc, SourceManager &SM) { + return SrcMgr::isSystem(NewFileType) && !SM.isInSystemHeader(PrevLoc); +} + +void HeaderIncludesJSONCallback::FileChanged( + SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind NewFileType, FileID PrevFID) { + if (PrevFID.isInvalid() || + !shouldRecordNewFile(NewFileType, SM.getLocForStartOfFile(PrevFID), SM)) + return; + + // Unless we are exiting a #include, make sure to skip ahead to the line the + // #include directive was at. + PresumedLoc UserLoc = SM.getPresumedLoc(Loc); + if (UserLoc.isInvalid()) + return; + + if (Reason == PPCallbacks::EnterFile && + UserLoc.getFilename() != StringRef("<command line>")) + IncludedHeaders.push_back(UserLoc.getFilename()); +} + +void HeaderIncludesJSONCallback::FileSkipped( + const FileEntryRef &SkippedFile, const Token &FilenameTok, + SrcMgr::CharacteristicKind FileType) { + if (!shouldRecordNewFile(FileType, FilenameTok.getLocation(), SM)) + return; + + IncludedHeaders.push_back(SkippedFile.getName().str()); +} |