@@ -106,6 +106,50 @@ 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 =
120+ llvm::DenseMap<FileEntryRef, SmallVector<FileEntryRef>>;
121+ DependencyMap Dependencies;
122+
123+ public:
124+ HeaderIncludesDirectPerFileCallback (const Preprocessor *PP,
125+ raw_ostream *OutputFile_,
126+ bool OwnsOutputFile_)
127+ : SM(PP->getSourceManager ()), HSI(PP->getHeaderSearchInfo ()),
128+ OutputFile(OutputFile_), OwnsOutputFile(OwnsOutputFile_) {}
129+
130+ ~HeaderIncludesDirectPerFileCallback () override {
131+ if (OwnsOutputFile)
132+ delete OutputFile;
133+ }
134+
135+ HeaderIncludesDirectPerFileCallback
136+ (const HeaderIncludesDirectPerFileCallback &) = delete;
137+ HeaderIncludesDirectPerFileCallback &
138+ operator =(const HeaderIncludesDirectPerFileCallback &) = delete ;
139+
140+ void EndOfMainFile () override ;
141+
142+ void InclusionDirective (SourceLocation HashLoc, const Token &IncludeTok,
143+ StringRef FileName, bool IsAngled,
144+ CharSourceRange FilenameRange,
145+ OptionalFileEntryRef File, StringRef SearchPath,
146+ StringRef RelativePath, const Module *SuggestedModule,
147+ bool ModuleImported,
148+ SrcMgr::CharacteristicKind FileType) override ;
149+
150+ void moduleImport (SourceLocation ImportLoc, ModuleIdPath Path,
151+ const Module *Imported) override ;
152+ };
109153}
110154
111155static void PrintHeaderInfo (raw_ostream *OutputFile, StringRef Filename,
@@ -192,14 +236,21 @@ void clang::AttachHeaderIncludeGen(Preprocessor &PP,
192236 MSStyle));
193237 break ;
194238 }
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));
239+ case HIFMT_JSON:
240+ switch (DepOpts.HeaderIncludeFiltering ) {
241+ default :
242+ llvm_unreachable (" Unknown HeaderIncludeFilteringKind enum" );
243+ case HIFIL_Only_Direct_System:
244+ PP.addPPCallbacks (std::make_unique<HeaderIncludesJSONCallback>(
245+ &PP, OutputFile, OwnsOutputFile));
246+ break ;
247+ case HIFIL_Direct_Per_File:
248+ PP.addPPCallbacks (std::make_unique<HeaderIncludesDirectPerFileCallback>(
249+ &PP, OutputFile, OwnsOutputFile));
250+ break ;
251+ }
200252 break ;
201253 }
202- }
203254}
204255
205256void HeaderIncludesCallback::FileChanged (SourceLocation Loc,
@@ -322,3 +373,80 @@ void HeaderIncludesJSONCallback::FileSkipped(
322373
323374 IncludedHeaders.push_back (SkippedFile.getName ().str ());
324375}
376+
377+ void HeaderIncludesDirectPerFileCallback::EndOfMainFile () {
378+ if (Dependencies.empty ())
379+ return ;
380+
381+ // Sort the files so that the output does not depend on the DenseMap order.
382+ SmallVector<FileEntryRef> SourceFiles;
383+ for (auto F = Dependencies.begin (), FEnd = Dependencies.end ();
384+ F != FEnd; ++F) {
385+ SourceFiles.push_back (F->first );
386+ }
387+ llvm::sort (SourceFiles, [](const FileEntryRef &LHS, const FileEntryRef &RHS) {
388+ return LHS.getUID () < RHS.getUID ();
389+ });
390+
391+ std::string Str;
392+ llvm::raw_string_ostream OS (Str);
393+ llvm::json::OStream JOS (OS);
394+ JOS.array ([&] {
395+ for (auto S = SourceFiles.begin (), SE = SourceFiles.end (); S != SE; ++S) {
396+ JOS.object ([&] {
397+ SmallVector<FileEntryRef> &Deps = Dependencies[*S];
398+ JOS.attribute (" source" , S->getName ().str ());
399+ JOS.attributeArray (" includes" , [&] {
400+ for (unsigned I = 0 , N = Deps.size (); I != N; ++I)
401+ JOS.value (Deps[I].getName ().str ());
402+ });
403+ });
404+ }
405+ });
406+ OS << " \n " ;
407+
408+ if (OutputFile->get_kind () == raw_ostream::OStreamKind::OK_FDStream) {
409+ llvm::raw_fd_ostream *FDS = static_cast <llvm::raw_fd_ostream *>(OutputFile);
410+ if (auto L = FDS->lock ())
411+ *OutputFile << Str;
412+ } else
413+ *OutputFile << Str;
414+ }
415+
416+ void HeaderIncludesDirectPerFileCallback::InclusionDirective (
417+ SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName,
418+ bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef File,
419+ StringRef SearchPath, StringRef RelativePath, const Module *SuggestedModule,
420+ bool ModuleImported, SrcMgr::CharacteristicKind FileType) {
421+ if (!File)
422+ return ;
423+
424+ SourceLocation Loc = SM.getExpansionLoc (HashLoc);
425+ if (SM.isInSystemHeader (Loc))
426+ return ;
427+ OptionalFileEntryRef FromFile = SM.getFileEntryRefForID (SM.getFileID (Loc));
428+ if (!FromFile)
429+ return ;
430+
431+ Dependencies[*FromFile].push_back (*File);
432+ }
433+
434+ void HeaderIncludesDirectPerFileCallback::moduleImport (
435+ SourceLocation ImportLoc, ModuleIdPath Path, 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