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.
53 /// Ideally `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, DiagOpts,
75 new ForwardingDiagnosticConsumer(Compiler.getDiagnosticClient())),
76 LangOpts(Compiler.getLangOpts()), HSOpts(Compiler.getHeaderSearchOpts()) {
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 VFS&: Compiler.getVirtualFileSystem());
86
87 LangOpts.Modules = false;
88
89 HeaderInfo = std::make_unique<HeaderSearch>(args&: HSOpts, args&: Sources, args&: Diags, args&: LangOpts,
90 args: &Compiler.getTarget());
91
92 PP = std::make_unique<clang::Preprocessor>(args&: Compiler.getPreprocessorOpts(),
93 args&: Diags, args&: LangOpts, args&: Sources,
94 args&: *HeaderInfo, args&: ModuleLoader,
95 /*IILookup=*/args: nullptr,
96 /*OwnsHeaderSearch=*/args: false);
97 PP->Initialize(Target: Compiler.getTarget(), AuxTarget: Compiler.getAuxTarget());
98 InitializePreprocessor(PP&: *PP, PPOpts: Compiler.getPreprocessorOpts(),
99 PCHContainerRdr: Compiler.getPCHContainerReader(),
100 FEOpts: Compiler.getFrontendOpts(), CodeGenOpts: Compiler.getCodeGenOpts());
101 ApplyHeaderSearchOptions(HS&: *HeaderInfo, HSOpts, Lang: LangOpts,
102 triple: Compiler.getTarget().getTriple());
103}
104
105ExpandModularHeadersPPCallbacks::~ExpandModularHeadersPPCallbacks() = default;
106
107Preprocessor *ExpandModularHeadersPPCallbacks::getPreprocessor() const {
108 return PP.get();
109}
110
111void ExpandModularHeadersPPCallbacks::handleModuleFile(
112 serialization::ModuleFile *MF) {
113 if (!MF)
114 return;
115 // Avoid processing a ModuleFile more than once.
116 if (!VisitedModules.insert(V: MF).second)
117 return;
118
119 // Visit all the input files of this module and mark them to record their
120 // contents later.
121 Compiler.getASTReader()->visitInputFiles(
122 MF&: *MF, IncludeSystem: true, Complain: false,
123 Visitor: [this](const serialization::InputFile &IF, bool /*IsSystem*/) {
124 Recorder->addNecessaryFile(File: *IF.getFile());
125 });
126 // Recursively handle all transitively imported modules.
127 for (auto *Import : MF->Imports)
128 handleModuleFile(MF: Import);
129}
130
131void ExpandModularHeadersPPCallbacks::parseToLocation(SourceLocation Loc) {
132 // Load all source locations present in the external sources.
133 for (unsigned I = 0, N = Sources.loaded_sloc_entry_size(); I != N; ++I) {
134 Sources.getLoadedSLocEntry(Index: I, Invalid: nullptr);
135 }
136 // Record contents of files we are interested in and add to the FileSystem.
137 for (auto It = Sources.fileinfo_begin(); It != Sources.fileinfo_end(); ++It) {
138 Recorder->recordFileContent(File: It->getFirst(), ContentCache: *It->getSecond(), InMemoryFs&: *InMemoryFs);
139 }
140 Recorder->checkAllFilesRecorded();
141
142 if (!StartedLexing) {
143 StartedLexing = true;
144 PP->Lex(Result&: CurrentToken);
145 }
146 while (!CurrentToken.is(K: tok::eof) &&
147 Sources.isBeforeInTranslationUnit(LHS: CurrentToken.getLocation(), RHS: Loc)) {
148 PP->Lex(Result&: CurrentToken);
149 }
150}
151
152void ExpandModularHeadersPPCallbacks::FileChanged(
153 SourceLocation Loc, FileChangeReason Reason,
154 SrcMgr::CharacteristicKind FileType, FileID PrevFID = FileID()) {
155 if (!EnteredMainFile) {
156 EnteredMainFile = true;
157 PP->EnterMainSourceFile();
158 }
159}
160
161void ExpandModularHeadersPPCallbacks::InclusionDirective(
162 SourceLocation DirectiveLoc, const Token &IncludeToken,
163 StringRef IncludedFilename, bool IsAngled, CharSourceRange FilenameRange,
164 OptionalFileEntryRef IncludedFile, StringRef SearchPath,
165 StringRef RelativePath, const Module *SuggestedModule, bool ModuleImported,
166 SrcMgr::CharacteristicKind FileType) {
167 if (ModuleImported) {
168 serialization::ModuleFile *MF =
169 Compiler.getASTReader()->getModuleManager().lookup(
170 File: *SuggestedModule->getASTFile());
171 handleModuleFile(MF);
172 }
173 parseToLocation(Loc: DirectiveLoc);
174}
175
176void ExpandModularHeadersPPCallbacks::EndOfMainFile() {
177 while (!CurrentToken.is(K: tok::eof))
178 PP->Lex(Result&: 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.
184void ExpandModularHeadersPPCallbacks::Ident(SourceLocation Loc, StringRef) {
185 parseToLocation(Loc);
186}
187void ExpandModularHeadersPPCallbacks::PragmaDirective(SourceLocation Loc,
188 PragmaIntroducerKind) {
189 parseToLocation(Loc);
190}
191void ExpandModularHeadersPPCallbacks::PragmaComment(SourceLocation Loc,
192 const IdentifierInfo *,
193 StringRef) {
194 parseToLocation(Loc);
195}
196void ExpandModularHeadersPPCallbacks::PragmaDetectMismatch(SourceLocation Loc,
197 StringRef,
198 StringRef) {
199 parseToLocation(Loc);
200}
201void ExpandModularHeadersPPCallbacks::PragmaDebug(SourceLocation Loc,
202 StringRef) {
203 parseToLocation(Loc);
204}
205void ExpandModularHeadersPPCallbacks::PragmaMessage(SourceLocation Loc,
206 StringRef,
207 PragmaMessageKind,
208 StringRef) {
209 parseToLocation(Loc);
210}
211void ExpandModularHeadersPPCallbacks::PragmaDiagnosticPush(SourceLocation Loc,
212 StringRef) {
213 parseToLocation(Loc);
214}
215void ExpandModularHeadersPPCallbacks::PragmaDiagnosticPop(SourceLocation Loc,
216 StringRef) {
217 parseToLocation(Loc);
218}
219void ExpandModularHeadersPPCallbacks::PragmaDiagnostic(SourceLocation Loc,
220 StringRef,
221 diag::Severity,
222 StringRef) {
223 parseToLocation(Loc);
224}
225void ExpandModularHeadersPPCallbacks::HasInclude(SourceLocation Loc, StringRef,
226 bool, OptionalFileEntryRef,
227 SrcMgr::CharacteristicKind) {
228 parseToLocation(Loc);
229}
230void 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(Loc: NameLoc);
235}
236void ExpandModularHeadersPPCallbacks::PragmaWarning(SourceLocation Loc,
237 PragmaWarningSpecifier,
238 ArrayRef<int>) {
239 parseToLocation(Loc);
240}
241void ExpandModularHeadersPPCallbacks::PragmaWarningPush(SourceLocation Loc,
242 int) {
243 parseToLocation(Loc);
244}
245void ExpandModularHeadersPPCallbacks::PragmaWarningPop(SourceLocation Loc) {
246 parseToLocation(Loc);
247}
248void ExpandModularHeadersPPCallbacks::PragmaAssumeNonNullBegin(
249 SourceLocation Loc) {
250 parseToLocation(Loc);
251}
252void ExpandModularHeadersPPCallbacks::PragmaAssumeNonNullEnd(
253 SourceLocation Loc) {
254 parseToLocation(Loc);
255}
256void ExpandModularHeadersPPCallbacks::MacroExpands(const Token &MacroNameTok,
257 const MacroDefinition &,
258 SourceRange Range,
259 const MacroArgs *) {
260 // FIXME: Figure out whether it's the right location to parse to.
261 parseToLocation(Loc: Range.getBegin());
262}
263void ExpandModularHeadersPPCallbacks::MacroDefined(const Token &MacroNameTok,
264 const MacroDirective *MD) {
265 parseToLocation(Loc: MD->getLocation());
266}
267void ExpandModularHeadersPPCallbacks::MacroUndefined(
268 const Token &, const MacroDefinition &, const MacroDirective *Undef) {
269 if (Undef)
270 parseToLocation(Loc: Undef->getLocation());
271}
272void ExpandModularHeadersPPCallbacks::Defined(const Token &MacroNameTok,
273 const MacroDefinition &,
274 SourceRange Range) {
275 // FIXME: Figure out whether it's the right location to parse to.
276 parseToLocation(Loc: Range.getBegin());
277}
278void ExpandModularHeadersPPCallbacks::SourceRangeSkipped(
279 SourceRange Range, SourceLocation EndifLoc) {
280 // FIXME: Figure out whether it's the right location to parse to.
281 parseToLocation(Loc: EndifLoc);
282}
283void ExpandModularHeadersPPCallbacks::If(SourceLocation Loc, SourceRange,
284 ConditionValueKind) {
285 parseToLocation(Loc);
286}
287void ExpandModularHeadersPPCallbacks::Elif(SourceLocation Loc, SourceRange,
288 ConditionValueKind, SourceLocation) {
289 parseToLocation(Loc);
290}
291void ExpandModularHeadersPPCallbacks::Ifdef(SourceLocation Loc, const Token &,
292 const MacroDefinition &) {
293 parseToLocation(Loc);
294}
295void ExpandModularHeadersPPCallbacks::Ifndef(SourceLocation Loc, const Token &,
296 const MacroDefinition &) {
297 parseToLocation(Loc);
298}
299void ExpandModularHeadersPPCallbacks::Else(SourceLocation Loc, SourceLocation) {
300 parseToLocation(Loc);
301}
302void ExpandModularHeadersPPCallbacks::Endif(SourceLocation Loc,
303 SourceLocation) {
304 parseToLocation(Loc);
305}
306
307} // namespace clang::tooling
308

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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