xref: /llvm-project/clang-tools-extra/clang-tidy/ExpandModularHeadersPPCallbacks.cpp (revision 5845688e91d85d46c0f47daaf4edfdfc772853cf)
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 #include <optional>
16 
17 #define DEBUG_TYPE "clang-tidy"
18 
19 namespace clang::tooling {
20 
21 class ExpandModularHeadersPPCallbacks::FileRecorder {
22 public:
23   /// Records that a given file entry is needed for replaying callbacks.
24   void addNecessaryFile(FileEntryRef File) {
25     // Don't record modulemap files because it breaks same file detection.
26     if (!(File.getName().ends_with("module.modulemap") ||
27           File.getName().ends_with("module.private.modulemap") ||
28           File.getName().ends_with("module.map") ||
29           File.getName().ends_with("module_private.map")))
30       FilesToRecord.insert(File);
31   }
32 
33   /// Records content for a file and adds it to the FileSystem.
34   void recordFileContent(FileEntryRef 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     std::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<FileEntryRef> 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   // FIXME: Investigate whatever is there better way to initialize DiagEngine
83   // or whatever DiagEngine can be shared by multiple preprocessors
84   ProcessWarningOptions(Diags, Compiler.getDiagnosticOpts(),
85                         Compiler.getVirtualFileSystem());
86 
87   LangOpts.Modules = false;
88 
89   auto HSO = std::make_shared<HeaderSearchOptions>();
90   *HSO = Compiler.getHeaderSearchOpts();
91 
92   HeaderInfo = std::make_unique<HeaderSearch>(HSO, Sources, Diags, LangOpts,
93                                                &Compiler.getTarget());
94 
95   auto PO = std::make_shared<PreprocessorOptions>();
96   *PO = Compiler.getPreprocessorOpts();
97 
98   PP = std::make_unique<clang::Preprocessor>(PO, Diags, LangOpts, Sources,
99                                               *HeaderInfo, ModuleLoader,
100                                               /*IILookup=*/nullptr,
101                                               /*OwnsHeaderSearch=*/false);
102   PP->Initialize(Compiler.getTarget(), Compiler.getAuxTarget());
103   InitializePreprocessor(*PP, *PO, Compiler.getPCHContainerReader(),
104                          Compiler.getFrontendOpts(), Compiler.getCodeGenOpts());
105   ApplyHeaderSearchOptions(*HeaderInfo, *HSO, LangOpts,
106                            Compiler.getTarget().getTriple());
107 }
108 
109 ExpandModularHeadersPPCallbacks::~ExpandModularHeadersPPCallbacks() = default;
110 
111 Preprocessor *ExpandModularHeadersPPCallbacks::getPreprocessor() const {
112   return PP.get();
113 }
114 
115 void ExpandModularHeadersPPCallbacks::handleModuleFile(
116     serialization::ModuleFile *MF) {
117   if (!MF)
118     return;
119   // Avoid processing a ModuleFile more than once.
120   if (!VisitedModules.insert(MF).second)
121     return;
122 
123   // Visit all the input files of this module and mark them to record their
124   // contents later.
125   Compiler.getASTReader()->visitInputFiles(
126       *MF, true, false,
127       [this](const serialization::InputFile &IF, bool /*IsSystem*/) {
128         Recorder->addNecessaryFile(*IF.getFile());
129       });
130   // Recursively handle all transitively imported modules.
131   for (auto *Import : MF->Imports)
132     handleModuleFile(Import);
133 }
134 
135 void ExpandModularHeadersPPCallbacks::parseToLocation(SourceLocation Loc) {
136   // Load all source locations present in the external sources.
137   for (unsigned I = 0, N = Sources.loaded_sloc_entry_size(); I != N; ++I) {
138     Sources.getLoadedSLocEntry(I, nullptr);
139   }
140   // Record contents of files we are interested in and add to the FileSystem.
141   for (auto It = Sources.fileinfo_begin(); It != Sources.fileinfo_end(); ++It) {
142     Recorder->recordFileContent(It->getFirst(), *It->getSecond(), *InMemoryFs);
143   }
144   Recorder->checkAllFilesRecorded();
145 
146   if (!StartedLexing) {
147     StartedLexing = true;
148     PP->Lex(CurrentToken);
149   }
150   while (!CurrentToken.is(tok::eof) &&
151          Sources.isBeforeInTranslationUnit(CurrentToken.getLocation(), Loc)) {
152     PP->Lex(CurrentToken);
153   }
154 }
155 
156 void ExpandModularHeadersPPCallbacks::FileChanged(
157     SourceLocation Loc, FileChangeReason Reason,
158     SrcMgr::CharacteristicKind FileType, FileID PrevFID = FileID()) {
159   if (!EnteredMainFile) {
160     EnteredMainFile = true;
161     PP->EnterMainSourceFile();
162   }
163 }
164 
165 void ExpandModularHeadersPPCallbacks::InclusionDirective(
166     SourceLocation DirectiveLoc, const Token &IncludeToken,
167     StringRef IncludedFilename, bool IsAngled, CharSourceRange FilenameRange,
168     OptionalFileEntryRef IncludedFile, StringRef SearchPath,
169     StringRef RelativePath, const Module *SuggestedModule, bool ModuleImported,
170     SrcMgr::CharacteristicKind FileType) {
171   if (ModuleImported) {
172     serialization::ModuleFile *MF =
173         Compiler.getASTReader()->getModuleManager().lookup(
174             *SuggestedModule->getASTFile());
175     handleModuleFile(MF);
176   }
177   parseToLocation(DirectiveLoc);
178 }
179 
180 void ExpandModularHeadersPPCallbacks::EndOfMainFile() {
181   while (!CurrentToken.is(tok::eof))
182     PP->Lex(CurrentToken);
183 }
184 
185 // Handle all other callbacks.
186 // Just parse to the corresponding location to generate the same callback for
187 // the PPCallbacks registered in our custom preprocessor.
188 void ExpandModularHeadersPPCallbacks::Ident(SourceLocation Loc, StringRef) {
189   parseToLocation(Loc);
190 }
191 void ExpandModularHeadersPPCallbacks::PragmaDirective(SourceLocation Loc,
192                                                       PragmaIntroducerKind) {
193   parseToLocation(Loc);
194 }
195 void ExpandModularHeadersPPCallbacks::PragmaComment(SourceLocation Loc,
196                                                     const IdentifierInfo *,
197                                                     StringRef) {
198   parseToLocation(Loc);
199 }
200 void ExpandModularHeadersPPCallbacks::PragmaDetectMismatch(SourceLocation Loc,
201                                                            StringRef,
202                                                            StringRef) {
203   parseToLocation(Loc);
204 }
205 void ExpandModularHeadersPPCallbacks::PragmaDebug(SourceLocation Loc,
206                                                   StringRef) {
207   parseToLocation(Loc);
208 }
209 void ExpandModularHeadersPPCallbacks::PragmaMessage(SourceLocation Loc,
210                                                     StringRef,
211                                                     PragmaMessageKind,
212                                                     StringRef) {
213   parseToLocation(Loc);
214 }
215 void ExpandModularHeadersPPCallbacks::PragmaDiagnosticPush(SourceLocation Loc,
216                                                            StringRef) {
217   parseToLocation(Loc);
218 }
219 void ExpandModularHeadersPPCallbacks::PragmaDiagnosticPop(SourceLocation Loc,
220                                                           StringRef) {
221   parseToLocation(Loc);
222 }
223 void ExpandModularHeadersPPCallbacks::PragmaDiagnostic(SourceLocation Loc,
224                                                        StringRef,
225                                                        diag::Severity,
226                                                        StringRef) {
227   parseToLocation(Loc);
228 }
229 void ExpandModularHeadersPPCallbacks::HasInclude(SourceLocation Loc, StringRef,
230                                                  bool, OptionalFileEntryRef,
231                                                  SrcMgr::CharacteristicKind) {
232   parseToLocation(Loc);
233 }
234 void ExpandModularHeadersPPCallbacks::PragmaOpenCLExtension(
235     SourceLocation NameLoc, const IdentifierInfo *, SourceLocation StateLoc,
236     unsigned) {
237   // FIXME: Figure out whether it's the right location to parse to.
238   parseToLocation(NameLoc);
239 }
240 void ExpandModularHeadersPPCallbacks::PragmaWarning(SourceLocation Loc,
241                                                     PragmaWarningSpecifier,
242                                                     ArrayRef<int>) {
243   parseToLocation(Loc);
244 }
245 void ExpandModularHeadersPPCallbacks::PragmaWarningPush(SourceLocation Loc,
246                                                         int) {
247   parseToLocation(Loc);
248 }
249 void ExpandModularHeadersPPCallbacks::PragmaWarningPop(SourceLocation Loc) {
250   parseToLocation(Loc);
251 }
252 void ExpandModularHeadersPPCallbacks::PragmaAssumeNonNullBegin(
253     SourceLocation Loc) {
254   parseToLocation(Loc);
255 }
256 void ExpandModularHeadersPPCallbacks::PragmaAssumeNonNullEnd(
257     SourceLocation Loc) {
258   parseToLocation(Loc);
259 }
260 void ExpandModularHeadersPPCallbacks::MacroExpands(const Token &MacroNameTok,
261                                                    const MacroDefinition &,
262                                                    SourceRange Range,
263                                                    const MacroArgs *) {
264   // FIXME: Figure out whether it's the right location to parse to.
265   parseToLocation(Range.getBegin());
266 }
267 void ExpandModularHeadersPPCallbacks::MacroDefined(const Token &MacroNameTok,
268                                                    const MacroDirective *MD) {
269   parseToLocation(MD->getLocation());
270 }
271 void ExpandModularHeadersPPCallbacks::MacroUndefined(
272     const Token &, const MacroDefinition &, const MacroDirective *Undef) {
273   if (Undef)
274     parseToLocation(Undef->getLocation());
275 }
276 void ExpandModularHeadersPPCallbacks::Defined(const Token &MacroNameTok,
277                                               const MacroDefinition &,
278                                               SourceRange Range) {
279   // FIXME: Figure out whether it's the right location to parse to.
280   parseToLocation(Range.getBegin());
281 }
282 void ExpandModularHeadersPPCallbacks::SourceRangeSkipped(
283     SourceRange Range, SourceLocation EndifLoc) {
284   // FIXME: Figure out whether it's the right location to parse to.
285   parseToLocation(EndifLoc);
286 }
287 void ExpandModularHeadersPPCallbacks::If(SourceLocation Loc, SourceRange,
288                                          ConditionValueKind) {
289   parseToLocation(Loc);
290 }
291 void ExpandModularHeadersPPCallbacks::Elif(SourceLocation Loc, SourceRange,
292                                            ConditionValueKind, SourceLocation) {
293   parseToLocation(Loc);
294 }
295 void ExpandModularHeadersPPCallbacks::Ifdef(SourceLocation Loc, const Token &,
296                                             const MacroDefinition &) {
297   parseToLocation(Loc);
298 }
299 void ExpandModularHeadersPPCallbacks::Ifndef(SourceLocation Loc, const Token &,
300                                              const MacroDefinition &) {
301   parseToLocation(Loc);
302 }
303 void ExpandModularHeadersPPCallbacks::Else(SourceLocation Loc, SourceLocation) {
304   parseToLocation(Loc);
305 }
306 void ExpandModularHeadersPPCallbacks::Endif(SourceLocation Loc,
307                                             SourceLocation) {
308   parseToLocation(Loc);
309 }
310 
311 } // namespace clang::tooling
312