clang-tools 22.0.0git
ExpandModularHeadersPPCallbacks.cpp
Go to the documentation of this file.
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
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
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("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.contains(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.
53 /// Ideally `FilesToRecord` should be empty.
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
68 CompilerInstance *CI, llvm::vfs::OverlayFileSystem &OverlayFS)
69 : Recorder(std::make_unique<FileRecorder>()), Compiler(*CI),
70 InMemoryFs(new llvm::vfs::InMemoryFileSystem),
71 Sources(Compiler.getSourceManager()),
72 // Forward the new diagnostics to the original DiagnosticConsumer.
73 Diags(DiagnosticIDs::create(), DiagOpts,
74 new ForwardingDiagnosticConsumer(Compiler.getDiagnosticClient())),
75 LangOpts(Compiler.getLangOpts()), HSOpts(Compiler.getHeaderSearchOpts()) {
76 // Add a FileSystem containing the extra files needed in place of modular
77 // headers.
78 OverlayFS.pushOverlay(InMemoryFs);
79
80 Diags.setSourceManager(&Sources);
81 // FIXME: Investigate whatever is there better way to initialize DiagEngine
82 // or whatever DiagEngine can be shared by multiple preprocessors
83 ProcessWarningOptions(Diags, Compiler.getDiagnosticOpts(),
84 Compiler.getVirtualFileSystem());
85
86 LangOpts.Modules = false;
87
88 HeaderInfo = std::make_unique<HeaderSearch>(HSOpts, Sources, Diags, LangOpts,
89 &Compiler.getTarget());
90
91 PP = std::make_unique<clang::Preprocessor>(Compiler.getPreprocessorOpts(),
92 Diags, LangOpts, Sources,
93 *HeaderInfo, ModuleLoader,
94 /*IILookup=*/nullptr,
95 /*OwnsHeaderSearch=*/false);
96 PP->Initialize(Compiler.getTarget(), Compiler.getAuxTarget());
97 InitializePreprocessor(*PP, Compiler.getPreprocessorOpts(),
98 Compiler.getPCHContainerReader(),
99 Compiler.getFrontendOpts(), Compiler.getCodeGenOpts());
100 ApplyHeaderSearchOptions(*HeaderInfo, HSOpts, LangOpts,
101 Compiler.getTarget().getTriple());
102}
103
105
107 return PP.get();
108}
109
110void ExpandModularHeadersPPCallbacks::handleModuleFile(
111 serialization::ModuleFile *MF) {
112 if (!MF)
113 return;
114 // Avoid processing a ModuleFile more than once.
115 if (!VisitedModules.insert(MF).second)
116 return;
117
118 // Visit all the input files of this module and mark them to record their
119 // contents later.
120 Compiler.getASTReader()->visitInputFiles(
121 *MF, true, false,
122 [this](const serialization::InputFile &IF, bool /*IsSystem*/) {
123 Recorder->addNecessaryFile(*IF.getFile());
124 });
125 // Recursively handle all transitively imported modules.
126 for (auto *Import : MF->Imports)
127 handleModuleFile(Import);
128}
129
130void ExpandModularHeadersPPCallbacks::parseToLocation(SourceLocation Loc) {
131 // Load all source locations present in the external sources.
132 for (unsigned I = 0, N = Sources.loaded_sloc_entry_size(); I != N; ++I) {
133 Sources.getLoadedSLocEntry(I, nullptr);
134 }
135 // Record contents of files we are interested in and add to the FileSystem.
136 for (auto It = Sources.fileinfo_begin(); It != Sources.fileinfo_end(); ++It) {
137 Recorder->recordFileContent(It->getFirst(), *It->getSecond(), *InMemoryFs);
138 }
139 Recorder->checkAllFilesRecorded();
140
141 if (!StartedLexing) {
142 StartedLexing = true;
143 PP->Lex(CurrentToken);
144 }
145 while (!CurrentToken.is(tok::eof) &&
146 Sources.isBeforeInTranslationUnit(CurrentToken.getLocation(), Loc)) {
147 PP->Lex(CurrentToken);
148 }
149}
150
151void ExpandModularHeadersPPCallbacks::FileChanged(
152 SourceLocation Loc, FileChangeReason Reason,
153 SrcMgr::CharacteristicKind FileType, FileID PrevFID = FileID()) {
154 if (!EnteredMainFile) {
155 EnteredMainFile = true;
156 PP->EnterMainSourceFile();
157 }
158}
159
160void ExpandModularHeadersPPCallbacks::InclusionDirective(
161 SourceLocation DirectiveLoc, const Token &IncludeToken,
162 StringRef IncludedFilename, bool IsAngled, CharSourceRange FilenameRange,
163 OptionalFileEntryRef IncludedFile, StringRef SearchPath,
164 StringRef RelativePath, const Module *SuggestedModule, bool ModuleImported,
165 SrcMgr::CharacteristicKind FileType) {
166 if (ModuleImported) {
167 serialization::ModuleFile *MF =
168 Compiler.getASTReader()->getModuleManager().lookup(
169 *SuggestedModule->getASTFile());
170 handleModuleFile(MF);
171 }
172 parseToLocation(DirectiveLoc);
173}
174
175void ExpandModularHeadersPPCallbacks::EndOfMainFile() {
176 while (!CurrentToken.is(tok::eof))
177 PP->Lex(CurrentToken);
178}
179
180// Handle all other callbacks.
181// Just parse to the corresponding location to generate the same callback for
182// the PPCallbacks registered in our custom preprocessor.
183void ExpandModularHeadersPPCallbacks::Ident(SourceLocation Loc, StringRef) {
184 parseToLocation(Loc);
185}
186void ExpandModularHeadersPPCallbacks::PragmaDirective(SourceLocation Loc,
187 PragmaIntroducerKind) {
188 parseToLocation(Loc);
189}
190void ExpandModularHeadersPPCallbacks::PragmaComment(SourceLocation Loc,
191 const IdentifierInfo *,
192 StringRef) {
193 parseToLocation(Loc);
194}
195void ExpandModularHeadersPPCallbacks::PragmaDetectMismatch(SourceLocation Loc,
196 StringRef,
197 StringRef) {
198 parseToLocation(Loc);
199}
200void ExpandModularHeadersPPCallbacks::PragmaDebug(SourceLocation Loc,
201 StringRef) {
202 parseToLocation(Loc);
203}
204void ExpandModularHeadersPPCallbacks::PragmaMessage(SourceLocation Loc,
205 StringRef,
206 PragmaMessageKind,
207 StringRef) {
208 parseToLocation(Loc);
209}
210void ExpandModularHeadersPPCallbacks::PragmaDiagnosticPush(SourceLocation Loc,
211 StringRef) {
212 parseToLocation(Loc);
213}
214void ExpandModularHeadersPPCallbacks::PragmaDiagnosticPop(SourceLocation Loc,
215 StringRef) {
216 parseToLocation(Loc);
217}
218void ExpandModularHeadersPPCallbacks::PragmaDiagnostic(SourceLocation Loc,
219 StringRef,
220 diag::Severity,
221 StringRef) {
222 parseToLocation(Loc);
223}
224void ExpandModularHeadersPPCallbacks::HasInclude(SourceLocation Loc, StringRef,
225 bool, OptionalFileEntryRef,
226 SrcMgr::CharacteristicKind) {
227 parseToLocation(Loc);
228}
229void ExpandModularHeadersPPCallbacks::PragmaOpenCLExtension(
230 SourceLocation NameLoc, const IdentifierInfo *, SourceLocation StateLoc,
231 unsigned) {
232 // FIXME: Figure out whether it's the right location to parse to.
233 parseToLocation(NameLoc);
234}
235void ExpandModularHeadersPPCallbacks::PragmaWarning(SourceLocation Loc,
236 PragmaWarningSpecifier,
237 ArrayRef<int>) {
238 parseToLocation(Loc);
239}
240void ExpandModularHeadersPPCallbacks::PragmaWarningPush(SourceLocation Loc,
241 int) {
242 parseToLocation(Loc);
243}
244void ExpandModularHeadersPPCallbacks::PragmaWarningPop(SourceLocation Loc) {
245 parseToLocation(Loc);
246}
247void ExpandModularHeadersPPCallbacks::PragmaAssumeNonNullBegin(
248 SourceLocation Loc) {
249 parseToLocation(Loc);
250}
251void ExpandModularHeadersPPCallbacks::PragmaAssumeNonNullEnd(
252 SourceLocation Loc) {
253 parseToLocation(Loc);
254}
255void 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}
262void ExpandModularHeadersPPCallbacks::MacroDefined(const Token &MacroNameTok,
263 const MacroDirective *MD) {
264 parseToLocation(MD->getLocation());
265}
266void ExpandModularHeadersPPCallbacks::MacroUndefined(
267 const Token &, const MacroDefinition &, const MacroDirective *Undef) {
268 if (Undef)
269 parseToLocation(Undef->getLocation());
270}
271void 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}
277void ExpandModularHeadersPPCallbacks::SourceRangeSkipped(
278 SourceRange Range, SourceLocation EndifLoc) {
279 // FIXME: Figure out whether it's the right location to parse to.
280 parseToLocation(EndifLoc);
281}
282void ExpandModularHeadersPPCallbacks::If(SourceLocation Loc, SourceRange,
283 ConditionValueKind) {
284 parseToLocation(Loc);
285}
286void ExpandModularHeadersPPCallbacks::Elif(SourceLocation Loc, SourceRange,
287 ConditionValueKind, SourceLocation) {
288 parseToLocation(Loc);
289}
290void ExpandModularHeadersPPCallbacks::Ifdef(SourceLocation Loc, const Token &,
291 const MacroDefinition &) {
292 parseToLocation(Loc);
293}
294void ExpandModularHeadersPPCallbacks::Ifndef(SourceLocation Loc, const Token &,
295 const MacroDefinition &) {
296 parseToLocation(Loc);
297}
298void ExpandModularHeadersPPCallbacks::Else(SourceLocation Loc, SourceLocation) {
299 parseToLocation(Loc);
300}
301void ExpandModularHeadersPPCallbacks::Endif(SourceLocation Loc,
302 SourceLocation) {
303 parseToLocation(Loc);
304}
305
306} // namespace clang::tooling
void addNecessaryFile(FileEntryRef File)
Records that a given file entry is needed for replaying callbacks.
void recordFileContent(FileEntryRef File, const SrcMgr::ContentCache &ContentCache, llvm::vfs::InMemoryFileSystem &InMemoryFs)
Records content for a file and adds it to the FileSystem.
void checkAllFilesRecorded()
Makes sure we have contents for all the files we were interested in.
ExpandModularHeadersPPCallbacks(CompilerInstance *CI, llvm::vfs::OverlayFileSystem &OverlayFS)
Preprocessor * getPreprocessor() const
Returns the preprocessor that provides callbacks for the whole translation unit, including the main f...
Some operations such as code completion produce a set of candidates.
Definition Generators.h:66