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