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