1 //===- ExpandModularHeadersPPCallbacks.h - clang-tidy -----------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "ExpandModularHeadersPPCallbacks.h"
10 #include "clang/Basic/FileManager.h"
11 #include "clang/Basic/TargetInfo.h"
12 #include "clang/Frontend/CompilerInstance.h"
13 #include "clang/Lex/PreprocessorOptions.h"
14 #include "clang/Serialization/ASTReader.h"
15 
16 namespace clang {
17 namespace tooling {
18 
19 class ExpandModularHeadersPPCallbacks::FileRecorder {
20 public:
21   /// Records that a given file entry is needed for replaying callbacks.
addNecessaryFile(const FileEntry * File)22   void addNecessaryFile(const FileEntry *File) { FilesToRecord.insert(File); }
23 
24   /// Records content for a file and adds it to the FileSystem.
recordFileContent(const FileEntry * File,const SrcMgr::ContentCache & ContentCache,llvm::vfs::InMemoryFileSystem & InMemoryFs)25   void recordFileContent(const FileEntry *File,
26                          const SrcMgr::ContentCache &ContentCache,
27                          llvm::vfs::InMemoryFileSystem &InMemoryFs) {
28     // Return if we are not interested in the contents of this file.
29     if (!FilesToRecord.count(File))
30       return;
31 
32     // FIXME: Why is this happening? We might be losing contents here.
33     if (!ContentCache.getRawBuffer())
34       return;
35 
36     InMemoryFs.addFile(File->getName(), /*ModificationTime=*/0,
37                        llvm::MemoryBuffer::getMemBufferCopy(
38                            ContentCache.getRawBuffer()->getBuffer()));
39     // Remove the file from the set of necessary files.
40     FilesToRecord.erase(File);
41   }
42 
43   /// Makes sure we have contents for all the files we were interested in. Ideally
44   /// `FilesToRecord` should be empty.
checkAllFilesRecorded()45   void checkAllFilesRecorded() {
46     for (auto FileEntry : FilesToRecord)
47       llvm::errs() << "Did not record contents for input file: "
48                    << FileEntry->getName() << "\n";
49   }
50 
51 private:
52   /// A set of files whose contents are to be recorded.
53   llvm::DenseSet<const FileEntry *> FilesToRecord;
54 };
55 
ExpandModularHeadersPPCallbacks(CompilerInstance * CI,IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFS)56 ExpandModularHeadersPPCallbacks::ExpandModularHeadersPPCallbacks(
57     CompilerInstance *CI,
58     IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFS)
59     : Recorder(std::make_unique<FileRecorder>()), Compiler(*CI),
60       InMemoryFs(new llvm::vfs::InMemoryFileSystem),
61       Sources(Compiler.getSourceManager()),
62       // Forward the new diagnostics to the original DiagnosticConsumer.
63       Diags(new DiagnosticIDs, new DiagnosticOptions,
64             new ForwardingDiagnosticConsumer(Compiler.getDiagnosticClient())),
65       LangOpts(Compiler.getLangOpts()) {
66   // Add a FileSystem containing the extra files needed in place of modular
67   // headers.
68   OverlayFS->pushOverlay(InMemoryFs);
69 
70   Diags.setSourceManager(&Sources);
71 
72   LangOpts.Modules = false;
73 
74   auto HSO = std::make_shared<HeaderSearchOptions>();
75   *HSO = Compiler.getHeaderSearchOpts();
76 
77   HeaderInfo = std::make_unique<HeaderSearch>(HSO, Sources, Diags, LangOpts,
78                                                &Compiler.getTarget());
79 
80   auto PO = std::make_shared<PreprocessorOptions>();
81   *PO = Compiler.getPreprocessorOpts();
82 
83   PP = std::make_unique<clang::Preprocessor>(PO, Diags, LangOpts, Sources,
84                                               *HeaderInfo, ModuleLoader,
85                                               /*IILookup=*/nullptr,
86                                               /*OwnsHeaderSearch=*/false);
87   PP->Initialize(Compiler.getTarget(), Compiler.getAuxTarget());
88   InitializePreprocessor(*PP, *PO, Compiler.getPCHContainerReader(),
89                          Compiler.getFrontendOpts());
90   ApplyHeaderSearchOptions(*HeaderInfo, *HSO, LangOpts,
91                            Compiler.getTarget().getTriple());
92 }
93 
94 ExpandModularHeadersPPCallbacks::~ExpandModularHeadersPPCallbacks() = default;
95 
getPreprocessor() const96 Preprocessor *ExpandModularHeadersPPCallbacks::getPreprocessor() const {
97   return PP.get();
98 }
99 
handleModuleFile(serialization::ModuleFile * MF)100 void ExpandModularHeadersPPCallbacks::handleModuleFile(
101     serialization::ModuleFile *MF) {
102   if (!MF)
103     return;
104   // Avoid processing a ModuleFile more than once.
105   if (VisitedModules.count(MF))
106     return;
107   VisitedModules.insert(MF);
108 
109   // Visit all the input files of this module and mark them to record their
110   // contents later.
111   Compiler.getASTReader()->visitInputFiles(
112       *MF, true, false,
113       [this](const serialization::InputFile &IF, bool /*IsSystem*/) {
114         Recorder->addNecessaryFile(IF.getFile());
115       });
116   // Recursively handle all transitively imported modules.
117   for (auto Import : MF->Imports)
118     handleModuleFile(Import);
119 }
120 
parseToLocation(SourceLocation Loc)121 void ExpandModularHeadersPPCallbacks::parseToLocation(SourceLocation Loc) {
122   // Load all source locations present in the external sources.
123   for (unsigned I = 0, N = Sources.loaded_sloc_entry_size(); I != N; ++I) {
124     Sources.getLoadedSLocEntry(I, nullptr);
125   }
126   // Record contents of files we are interested in and add to the FileSystem.
127   for (auto It = Sources.fileinfo_begin(); It != Sources.fileinfo_end(); ++It) {
128     Recorder->recordFileContent(It->getFirst(), *It->getSecond(), *InMemoryFs);
129   }
130   Recorder->checkAllFilesRecorded();
131 
132   if (!StartedLexing) {
133     StartedLexing = true;
134     PP->Lex(CurrentToken);
135   }
136   while (!CurrentToken.is(tok::eof) &&
137          Sources.isBeforeInTranslationUnit(CurrentToken.getLocation(), Loc)) {
138     PP->Lex(CurrentToken);
139   }
140 }
141 
FileChanged(SourceLocation Loc,FileChangeReason Reason,SrcMgr::CharacteristicKind FileType,FileID PrevFID=FileID ())142 void ExpandModularHeadersPPCallbacks::FileChanged(
143     SourceLocation Loc, FileChangeReason Reason,
144     SrcMgr::CharacteristicKind FileType, FileID PrevFID = FileID()) {
145   if (!EnteredMainFile) {
146     EnteredMainFile = true;
147     PP->EnterMainSourceFile();
148   }
149 }
150 
InclusionDirective(SourceLocation DirectiveLoc,const Token & IncludeToken,StringRef IncludedFilename,bool IsAngled,CharSourceRange FilenameRange,const FileEntry * IncludedFile,StringRef SearchPath,StringRef RelativePath,const Module * Imported,SrcMgr::CharacteristicKind FileType)151 void ExpandModularHeadersPPCallbacks::InclusionDirective(
152     SourceLocation DirectiveLoc, const Token &IncludeToken,
153     StringRef IncludedFilename, bool IsAngled, CharSourceRange FilenameRange,
154     const FileEntry *IncludedFile, StringRef SearchPath, StringRef RelativePath,
155     const Module *Imported, SrcMgr::CharacteristicKind FileType) {
156   if (Imported) {
157     serialization::ModuleFile *MF =
158         Compiler.getASTReader()->getModuleManager().lookup(
159             Imported->getASTFile());
160     handleModuleFile(MF);
161   }
162   parseToLocation(DirectiveLoc);
163 }
164 
EndOfMainFile()165 void ExpandModularHeadersPPCallbacks::EndOfMainFile() {
166   while (!CurrentToken.is(tok::eof))
167     PP->Lex(CurrentToken);
168 }
169 
170 // Handle all other callbacks.
171 // Just parse to the corresponding location to generate the same callback for
172 // the PPCallbacks registered in our custom preprocessor.
Ident(SourceLocation Loc,StringRef)173 void ExpandModularHeadersPPCallbacks::Ident(SourceLocation Loc, StringRef) {
174   parseToLocation(Loc);
175 }
PragmaDirective(SourceLocation Loc,PragmaIntroducerKind)176 void ExpandModularHeadersPPCallbacks::PragmaDirective(SourceLocation Loc,
177                                                       PragmaIntroducerKind) {
178   parseToLocation(Loc);
179 }
PragmaComment(SourceLocation Loc,const IdentifierInfo *,StringRef)180 void ExpandModularHeadersPPCallbacks::PragmaComment(SourceLocation Loc,
181                                                     const IdentifierInfo *,
182                                                     StringRef) {
183   parseToLocation(Loc);
184 }
PragmaDetectMismatch(SourceLocation Loc,StringRef,StringRef)185 void ExpandModularHeadersPPCallbacks::PragmaDetectMismatch(SourceLocation Loc,
186                                                            StringRef,
187                                                            StringRef) {
188   parseToLocation(Loc);
189 }
PragmaDebug(SourceLocation Loc,StringRef)190 void ExpandModularHeadersPPCallbacks::PragmaDebug(SourceLocation Loc,
191                                                   StringRef) {
192   parseToLocation(Loc);
193 }
PragmaMessage(SourceLocation Loc,StringRef,PragmaMessageKind,StringRef)194 void ExpandModularHeadersPPCallbacks::PragmaMessage(SourceLocation Loc,
195                                                     StringRef,
196                                                     PragmaMessageKind,
197                                                     StringRef) {
198   parseToLocation(Loc);
199 }
PragmaDiagnosticPush(SourceLocation Loc,StringRef)200 void ExpandModularHeadersPPCallbacks::PragmaDiagnosticPush(SourceLocation Loc,
201                                                            StringRef) {
202   parseToLocation(Loc);
203 }
PragmaDiagnosticPop(SourceLocation Loc,StringRef)204 void ExpandModularHeadersPPCallbacks::PragmaDiagnosticPop(SourceLocation Loc,
205                                                           StringRef) {
206   parseToLocation(Loc);
207 }
PragmaDiagnostic(SourceLocation Loc,StringRef,diag::Severity,StringRef)208 void ExpandModularHeadersPPCallbacks::PragmaDiagnostic(SourceLocation Loc,
209                                                        StringRef,
210                                                        diag::Severity,
211                                                        StringRef) {
212   parseToLocation(Loc);
213 }
HasInclude(SourceLocation Loc,StringRef,bool,Optional<FileEntryRef>,SrcMgr::CharacteristicKind)214 void ExpandModularHeadersPPCallbacks::HasInclude(SourceLocation Loc, StringRef,
215                                                  bool, Optional<FileEntryRef>,
216                                                  SrcMgr::CharacteristicKind) {
217   parseToLocation(Loc);
218 }
PragmaOpenCLExtension(SourceLocation NameLoc,const IdentifierInfo *,SourceLocation StateLoc,unsigned)219 void ExpandModularHeadersPPCallbacks::PragmaOpenCLExtension(
220     SourceLocation NameLoc, const IdentifierInfo *, SourceLocation StateLoc,
221     unsigned) {
222   // FIME: Figure out whether it's the right location to parse to.
223   parseToLocation(NameLoc);
224 }
PragmaWarning(SourceLocation Loc,StringRef,ArrayRef<int>)225 void ExpandModularHeadersPPCallbacks::PragmaWarning(SourceLocation Loc,
226                                                     StringRef, ArrayRef<int>) {
227   parseToLocation(Loc);
228 }
PragmaWarningPush(SourceLocation Loc,int)229 void ExpandModularHeadersPPCallbacks::PragmaWarningPush(SourceLocation Loc,
230                                                         int) {
231   parseToLocation(Loc);
232 }
PragmaWarningPop(SourceLocation Loc)233 void ExpandModularHeadersPPCallbacks::PragmaWarningPop(SourceLocation Loc) {
234   parseToLocation(Loc);
235 }
PragmaAssumeNonNullBegin(SourceLocation Loc)236 void ExpandModularHeadersPPCallbacks::PragmaAssumeNonNullBegin(
237     SourceLocation Loc) {
238   parseToLocation(Loc);
239 }
PragmaAssumeNonNullEnd(SourceLocation Loc)240 void ExpandModularHeadersPPCallbacks::PragmaAssumeNonNullEnd(
241     SourceLocation Loc) {
242   parseToLocation(Loc);
243 }
MacroExpands(const Token & MacroNameTok,const MacroDefinition &,SourceRange Range,const MacroArgs *)244 void ExpandModularHeadersPPCallbacks::MacroExpands(const Token &MacroNameTok,
245                                                    const MacroDefinition &,
246                                                    SourceRange Range,
247                                                    const MacroArgs *) {
248   // FIME: Figure out whether it's the right location to parse to.
249   parseToLocation(Range.getBegin());
250 }
MacroDefined(const Token & MacroNameTok,const MacroDirective * MD)251 void ExpandModularHeadersPPCallbacks::MacroDefined(const Token &MacroNameTok,
252                                                    const MacroDirective *MD) {
253   parseToLocation(MD->getLocation());
254 }
MacroUndefined(const Token &,const MacroDefinition &,const MacroDirective * Undef)255 void ExpandModularHeadersPPCallbacks::MacroUndefined(
256     const Token &, const MacroDefinition &, const MacroDirective *Undef) {
257   if (Undef)
258     parseToLocation(Undef->getLocation());
259 }
Defined(const Token & MacroNameTok,const MacroDefinition &,SourceRange Range)260 void ExpandModularHeadersPPCallbacks::Defined(const Token &MacroNameTok,
261                                               const MacroDefinition &,
262                                               SourceRange Range) {
263   // FIME: Figure out whether it's the right location to parse to.
264   parseToLocation(Range.getBegin());
265 }
SourceRangeSkipped(SourceRange Range,SourceLocation EndifLoc)266 void ExpandModularHeadersPPCallbacks::SourceRangeSkipped(
267     SourceRange Range, SourceLocation EndifLoc) {
268   // FIME: Figure out whether it's the right location to parse to.
269   parseToLocation(EndifLoc);
270 }
If(SourceLocation Loc,SourceRange,ConditionValueKind)271 void ExpandModularHeadersPPCallbacks::If(SourceLocation Loc, SourceRange,
272                                          ConditionValueKind) {
273   parseToLocation(Loc);
274 }
Elif(SourceLocation Loc,SourceRange,ConditionValueKind,SourceLocation)275 void ExpandModularHeadersPPCallbacks::Elif(SourceLocation Loc, SourceRange,
276                                            ConditionValueKind, SourceLocation) {
277   parseToLocation(Loc);
278 }
Ifdef(SourceLocation Loc,const Token &,const MacroDefinition &)279 void ExpandModularHeadersPPCallbacks::Ifdef(SourceLocation Loc, const Token &,
280                                             const MacroDefinition &) {
281   parseToLocation(Loc);
282 }
Ifndef(SourceLocation Loc,const Token &,const MacroDefinition &)283 void ExpandModularHeadersPPCallbacks::Ifndef(SourceLocation Loc, const Token &,
284                                              const MacroDefinition &) {
285   parseToLocation(Loc);
286 }
Else(SourceLocation Loc,SourceLocation)287 void ExpandModularHeadersPPCallbacks::Else(SourceLocation Loc, SourceLocation) {
288   parseToLocation(Loc);
289 }
Endif(SourceLocation Loc,SourceLocation)290 void ExpandModularHeadersPPCallbacks::Endif(SourceLocation Loc,
291                                             SourceLocation) {
292   parseToLocation(Loc);
293 }
294 
295 } // namespace tooling
296 } // namespace clang
297