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