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
19namespace clang::tooling {
20
21class ExpandModularHeadersPPCallbacks::FileRecorder {
22public:
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(Suffix: "module.modulemap") ||
27 File.getName().ends_with(Suffix: "module.private.modulemap") ||
28 File.getName().ends_with(Suffix: "module.map") ||
29 File.getName().ends_with(Suffix: "module_private.map")))
30 FilesToRecord.insert(V: 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(V: 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(Path: File.getName(), /*ModificationTime=*/0,
47 Buffer: llvm::MemoryBuffer::getMemBufferCopy(InputData: *Data));
48 // Remove the file from the set of necessary files.
49 FilesToRecord.erase(V: 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
62private:
63 /// A set of files whose contents are to be recorded.
64 llvm::DenseSet<FileEntryRef> FilesToRecord;
65};
66
67ExpandModularHeadersPPCallbacks::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(FS: 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, Opts: Compiler.getDiagnosticOpts());
85
86 LangOpts.Modules = false;
87
88 auto HSO = std::make_shared<HeaderSearchOptions>();
89 *HSO = Compiler.getHeaderSearchOpts();
90
91 HeaderInfo = std::make_unique<HeaderSearch>(args&: HSO, args&: Sources, args&: Diags, args&: LangOpts,
92 args: &Compiler.getTarget());
93
94 auto PO = std::make_shared<PreprocessorOptions>();
95 *PO = Compiler.getPreprocessorOpts();
96
97 PP = std::make_unique<clang::Preprocessor>(args&: PO, args&: Diags, args&: LangOpts, args&: Sources,
98 args&: *HeaderInfo, args&: ModuleLoader,
99 /*IILookup=*/args: nullptr,
100 /*OwnsHeaderSearch=*/args: false);
101 PP->Initialize(Target: Compiler.getTarget(), AuxTarget: Compiler.getAuxTarget());
102 InitializePreprocessor(PP&: *PP, PPOpts: *PO, PCHContainerRdr: Compiler.getPCHContainerReader(),
103 FEOpts: Compiler.getFrontendOpts(), CodeGenOpts: Compiler.getCodeGenOpts());
104 ApplyHeaderSearchOptions(HS&: *HeaderInfo, HSOpts: *HSO, Lang: LangOpts,
105 triple: Compiler.getTarget().getTriple());
106}
107
108ExpandModularHeadersPPCallbacks::~ExpandModularHeadersPPCallbacks() = default;
109
110Preprocessor *ExpandModularHeadersPPCallbacks::getPreprocessor() const {
111 return PP.get();
112}
113
114void ExpandModularHeadersPPCallbacks::handleModuleFile(
115 serialization::ModuleFile *MF) {
116 if (!MF)
117 return;
118 // Avoid processing a ModuleFile more than once.
119 if (VisitedModules.count(V: MF))
120 return;
121 VisitedModules.insert(V: MF);
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&: *MF, IncludeSystem: true, Complain: false,
127 Visitor: [this](const serialization::InputFile &IF, bool /*IsSystem*/) {
128 Recorder->addNecessaryFile(File: *IF.getFile());
129 });
130 // Recursively handle all transitively imported modules.
131 for (auto *Import : MF->Imports)
132 handleModuleFile(MF: Import);
133}
134
135void 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(Index: I, Invalid: 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(File: It->getFirst(), ContentCache: *It->getSecond(), InMemoryFs&: *InMemoryFs);
143 }
144 Recorder->checkAllFilesRecorded();
145
146 if (!StartedLexing) {
147 StartedLexing = true;
148 PP->Lex(Result&: CurrentToken);
149 }
150 while (!CurrentToken.is(K: tok::eof) &&
151 Sources.isBeforeInTranslationUnit(LHS: CurrentToken.getLocation(), RHS: Loc)) {
152 PP->Lex(Result&: CurrentToken);
153 }
154}
155
156void 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
165void 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 File: *SuggestedModule->getASTFile());
175 handleModuleFile(MF);
176 }
177 parseToLocation(Loc: DirectiveLoc);
178}
179
180void ExpandModularHeadersPPCallbacks::EndOfMainFile() {
181 while (!CurrentToken.is(K: tok::eof))
182 PP->Lex(Result&: 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.
188void ExpandModularHeadersPPCallbacks::Ident(SourceLocation Loc, StringRef) {
189 parseToLocation(Loc);
190}
191void ExpandModularHeadersPPCallbacks::PragmaDirective(SourceLocation Loc,
192 PragmaIntroducerKind) {
193 parseToLocation(Loc);
194}
195void ExpandModularHeadersPPCallbacks::PragmaComment(SourceLocation Loc,
196 const IdentifierInfo *,
197 StringRef) {
198 parseToLocation(Loc);
199}
200void ExpandModularHeadersPPCallbacks::PragmaDetectMismatch(SourceLocation Loc,
201 StringRef,
202 StringRef) {
203 parseToLocation(Loc);
204}
205void ExpandModularHeadersPPCallbacks::PragmaDebug(SourceLocation Loc,
206 StringRef) {
207 parseToLocation(Loc);
208}
209void ExpandModularHeadersPPCallbacks::PragmaMessage(SourceLocation Loc,
210 StringRef,
211 PragmaMessageKind,
212 StringRef) {
213 parseToLocation(Loc);
214}
215void ExpandModularHeadersPPCallbacks::PragmaDiagnosticPush(SourceLocation Loc,
216 StringRef) {
217 parseToLocation(Loc);
218}
219void ExpandModularHeadersPPCallbacks::PragmaDiagnosticPop(SourceLocation Loc,
220 StringRef) {
221 parseToLocation(Loc);
222}
223void ExpandModularHeadersPPCallbacks::PragmaDiagnostic(SourceLocation Loc,
224 StringRef,
225 diag::Severity,
226 StringRef) {
227 parseToLocation(Loc);
228}
229void ExpandModularHeadersPPCallbacks::HasInclude(SourceLocation Loc, StringRef,
230 bool, OptionalFileEntryRef,
231 SrcMgr::CharacteristicKind) {
232 parseToLocation(Loc);
233}
234void 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(Loc: NameLoc);
239}
240void ExpandModularHeadersPPCallbacks::PragmaWarning(SourceLocation Loc,
241 PragmaWarningSpecifier,
242 ArrayRef<int>) {
243 parseToLocation(Loc);
244}
245void ExpandModularHeadersPPCallbacks::PragmaWarningPush(SourceLocation Loc,
246 int) {
247 parseToLocation(Loc);
248}
249void ExpandModularHeadersPPCallbacks::PragmaWarningPop(SourceLocation Loc) {
250 parseToLocation(Loc);
251}
252void ExpandModularHeadersPPCallbacks::PragmaAssumeNonNullBegin(
253 SourceLocation Loc) {
254 parseToLocation(Loc);
255}
256void ExpandModularHeadersPPCallbacks::PragmaAssumeNonNullEnd(
257 SourceLocation Loc) {
258 parseToLocation(Loc);
259}
260void 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(Loc: Range.getBegin());
266}
267void ExpandModularHeadersPPCallbacks::MacroDefined(const Token &MacroNameTok,
268 const MacroDirective *MD) {
269 parseToLocation(Loc: MD->getLocation());
270}
271void ExpandModularHeadersPPCallbacks::MacroUndefined(
272 const Token &, const MacroDefinition &, const MacroDirective *Undef) {
273 if (Undef)
274 parseToLocation(Loc: Undef->getLocation());
275}
276void 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(Loc: Range.getBegin());
281}
282void ExpandModularHeadersPPCallbacks::SourceRangeSkipped(
283 SourceRange Range, SourceLocation EndifLoc) {
284 // FIXME: Figure out whether it's the right location to parse to.
285 parseToLocation(Loc: EndifLoc);
286}
287void ExpandModularHeadersPPCallbacks::If(SourceLocation Loc, SourceRange,
288 ConditionValueKind) {
289 parseToLocation(Loc);
290}
291void ExpandModularHeadersPPCallbacks::Elif(SourceLocation Loc, SourceRange,
292 ConditionValueKind, SourceLocation) {
293 parseToLocation(Loc);
294}
295void ExpandModularHeadersPPCallbacks::Ifdef(SourceLocation Loc, const Token &,
296 const MacroDefinition &) {
297 parseToLocation(Loc);
298}
299void ExpandModularHeadersPPCallbacks::Ifndef(SourceLocation Loc, const Token &,
300 const MacroDefinition &) {
301 parseToLocation(Loc);
302}
303void ExpandModularHeadersPPCallbacks::Else(SourceLocation Loc, SourceLocation) {
304 parseToLocation(Loc);
305}
306void ExpandModularHeadersPPCallbacks::Endif(SourceLocation Loc,
307 SourceLocation) {
308 parseToLocation(Loc);
309}
310
311} // namespace clang::tooling
312

source code of clang-tools-extra/clang-tidy/ExpandModularHeadersPPCallbacks.cpp