@@ -106,6 +106,49 @@ class HeaderIncludesJSONCallback : public PPCallbacks {
106106 void FileSkipped (const FileEntryRef &SkippedFile, const Token &FilenameTok,
107107 SrcMgr::CharacteristicKind FileType) override ;
108108};
109+
110+ // / A callback for emitting direct header and module usage information to a
111+ // / file in JSON. The output format is like HeaderIncludesJSONCallback but has
112+ // / an array of separate entries, one for each non-system source file used in
113+ // / the compilation showing only the direct includes and imports from that file.
114+ class HeaderIncludesDirectPerFileCallback : public PPCallbacks {
115+ SourceManager &SM;
116+ HeaderSearch &HSI;
117+ raw_ostream *OutputFile;
118+ bool OwnsOutputFile;
119+ using DependencyMap = llvm::DenseMap<FileEntryRef, SmallVector<FileEntryRef>>;
120+ DependencyMap Dependencies;
121+
122+ public:
123+ HeaderIncludesDirectPerFileCallback (const Preprocessor *PP,
124+ raw_ostream *OutputFile_,
125+ bool OwnsOutputFile_)
126+ : SM(PP->getSourceManager ()), HSI(PP->getHeaderSearchInfo ()),
127+ OutputFile(OutputFile_), OwnsOutputFile(OwnsOutputFile_) {}
128+
129+ ~HeaderIncludesDirectPerFileCallback () override {
130+ if (OwnsOutputFile)
131+ delete OutputFile;
132+ }
133+
134+ HeaderIncludesDirectPerFileCallback (
135+ const HeaderIncludesDirectPerFileCallback &) = delete;
136+ HeaderIncludesDirectPerFileCallback &
137+ operator =(const HeaderIncludesDirectPerFileCallback &) = delete;
138+
139+ void EndOfMainFile () override ;
140+
141+ void InclusionDirective (SourceLocation HashLoc, const Token &IncludeTok,
142+ StringRef FileName, bool IsAngled,
143+ CharSourceRange FilenameRange,
144+ OptionalFileEntryRef File, StringRef SearchPath,
145+ StringRef RelativePath, const Module *SuggestedModule,
146+ bool ModuleImported,
147+ SrcMgr::CharacteristicKind FileType) override ;
148+
149+ void moduleImport (SourceLocation ImportLoc, ModuleIdPath Path,
150+ const Module *Imported) override ;
151+ };
109152}
110153
111154static void PrintHeaderInfo (raw_ostream *OutputFile, StringRef Filename,
@@ -192,14 +235,21 @@ void clang::AttachHeaderIncludeGen(Preprocessor &PP,
192235 MSStyle));
193236 break ;
194237 }
195- case HIFMT_JSON: {
196- assert (DepOpts.HeaderIncludeFiltering == HIFIL_Only_Direct_System &&
197- " only-direct-system is the only option for filtering" );
198- PP.addPPCallbacks (std::make_unique<HeaderIncludesJSONCallback>(
199- &PP, OutputFile, OwnsOutputFile));
238+ case HIFMT_JSON:
239+ switch (DepOpts.HeaderIncludeFiltering ) {
240+ default :
241+ llvm_unreachable (" Unknown HeaderIncludeFilteringKind enum" );
242+ case HIFIL_Only_Direct_System:
243+ PP.addPPCallbacks (std::make_unique<HeaderIncludesJSONCallback>(
244+ &PP, OutputFile, OwnsOutputFile));
245+ break ;
246+ case HIFIL_Direct_Per_File:
247+ PP.addPPCallbacks (std::make_unique<HeaderIncludesDirectPerFileCallback>(
248+ &PP, OutputFile, OwnsOutputFile));
249+ break ;
250+ }
200251 break ;
201252 }
202- }
203253}
204254
205255void HeaderIncludesCallback::FileChanged (SourceLocation Loc,
@@ -322,3 +372,81 @@ void HeaderIncludesJSONCallback::FileSkipped(
322372
323373 IncludedHeaders.push_back (SkippedFile.getName ().str ());
324374}
375+
376+ void HeaderIncludesDirectPerFileCallback::EndOfMainFile () {
377+ if (Dependencies.empty ())
378+ return ;
379+
380+ // Sort the files so that the output does not depend on the DenseMap order.
381+ SmallVector<FileEntryRef> SourceFiles;
382+ for (auto F = Dependencies.begin (), FEnd = Dependencies.end (); F != FEnd;
383+ ++F) {
384+ SourceFiles.push_back (F->first );
385+ }
386+ llvm::sort (SourceFiles, [](const FileEntryRef &LHS, const FileEntryRef &RHS) {
387+ return LHS.getUID () < RHS.getUID ();
388+ });
389+
390+ std::string Str;
391+ llvm::raw_string_ostream OS (Str);
392+ llvm::json::OStream JOS (OS);
393+ JOS.array ([&] {
394+ for (auto S = SourceFiles.begin (), SE = SourceFiles.end (); S != SE; ++S) {
395+ JOS.object ([&] {
396+ SmallVector<FileEntryRef> &Deps = Dependencies[*S];
397+ JOS.attribute (" source" , S->getName ().str ());
398+ JOS.attributeArray (" includes" , [&] {
399+ for (unsigned I = 0 , N = Deps.size (); I != N; ++I)
400+ JOS.value (Deps[I].getName ().str ());
401+ });
402+ });
403+ }
404+ });
405+ OS << " \n " ;
406+
407+ if (OutputFile->get_kind () == raw_ostream::OStreamKind::OK_FDStream) {
408+ llvm::raw_fd_ostream *FDS = static_cast <llvm::raw_fd_ostream *>(OutputFile);
409+ if (auto L = FDS->lock ())
410+ *OutputFile << Str;
411+ } else
412+ *OutputFile << Str;
413+ }
414+
415+ void HeaderIncludesDirectPerFileCallback::InclusionDirective (
416+ SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName,
417+ bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef File,
418+ StringRef SearchPath, StringRef RelativePath, const Module *SuggestedModule,
419+ bool ModuleImported, SrcMgr::CharacteristicKind FileType) {
420+ if (!File)
421+ return ;
422+
423+ SourceLocation Loc = SM.getExpansionLoc (HashLoc);
424+ if (SM.isInSystemHeader (Loc))
425+ return ;
426+ OptionalFileEntryRef FromFile = SM.getFileEntryRefForID (SM.getFileID (Loc));
427+ if (!FromFile)
428+ return ;
429+
430+ Dependencies[*FromFile].push_back (*File);
431+ }
432+
433+ void HeaderIncludesDirectPerFileCallback::moduleImport (SourceLocation ImportLoc,
434+ ModuleIdPath Path,
435+ const Module *Imported) {
436+ if (!Imported)
437+ return ;
438+
439+ SourceLocation Loc = SM.getExpansionLoc (ImportLoc);
440+ if (SM.isInSystemHeader (Loc))
441+ return ;
442+ OptionalFileEntryRef FromFile = SM.getFileEntryRefForID (SM.getFileID (Loc));
443+ if (!FromFile)
444+ return ;
445+
446+ OptionalFileEntryRef ModuleMapFile =
447+ HSI.getModuleMap ().getModuleMapFileForUniquing (Imported);
448+ if (!ModuleMapFile)
449+ return ;
450+
451+ Dependencies[*FromFile].push_back (*ModuleMapFile);
452+ }
0 commit comments