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 | |
19 | namespace clang::tooling { |
20 | |
21 | class ExpandModularHeadersPPCallbacks::FileRecorder { |
22 | public: |
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 | |
62 | private: |
63 | /// A set of files whose contents are to be recorded. |
64 | llvm::DenseSet<FileEntryRef> FilesToRecord; |
65 | }; |
66 | |
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(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 | |
108 | ExpandModularHeadersPPCallbacks::~ExpandModularHeadersPPCallbacks() = default; |
109 | |
110 | Preprocessor *ExpandModularHeadersPPCallbacks::getPreprocessor() const { |
111 | return PP.get(); |
112 | } |
113 | |
114 | void 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 | |
135 | void 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 | |
156 | void 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 | |
165 | void 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 | |
180 | void 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. |
188 | void ExpandModularHeadersPPCallbacks::Ident(SourceLocation Loc, StringRef) { |
189 | parseToLocation(Loc); |
190 | } |
191 | void ExpandModularHeadersPPCallbacks::PragmaDirective(SourceLocation Loc, |
192 | PragmaIntroducerKind) { |
193 | parseToLocation(Loc); |
194 | } |
195 | void ExpandModularHeadersPPCallbacks::PragmaComment(SourceLocation Loc, |
196 | const IdentifierInfo *, |
197 | StringRef) { |
198 | parseToLocation(Loc); |
199 | } |
200 | void ExpandModularHeadersPPCallbacks::PragmaDetectMismatch(SourceLocation Loc, |
201 | StringRef, |
202 | StringRef) { |
203 | parseToLocation(Loc); |
204 | } |
205 | void ExpandModularHeadersPPCallbacks::PragmaDebug(SourceLocation Loc, |
206 | StringRef) { |
207 | parseToLocation(Loc); |
208 | } |
209 | void ExpandModularHeadersPPCallbacks::PragmaMessage(SourceLocation Loc, |
210 | StringRef, |
211 | PragmaMessageKind, |
212 | StringRef) { |
213 | parseToLocation(Loc); |
214 | } |
215 | void ExpandModularHeadersPPCallbacks::PragmaDiagnosticPush(SourceLocation Loc, |
216 | StringRef) { |
217 | parseToLocation(Loc); |
218 | } |
219 | void ExpandModularHeadersPPCallbacks::PragmaDiagnosticPop(SourceLocation Loc, |
220 | StringRef) { |
221 | parseToLocation(Loc); |
222 | } |
223 | void ExpandModularHeadersPPCallbacks::PragmaDiagnostic(SourceLocation Loc, |
224 | StringRef, |
225 | diag::Severity, |
226 | StringRef) { |
227 | parseToLocation(Loc); |
228 | } |
229 | void ExpandModularHeadersPPCallbacks::HasInclude(SourceLocation Loc, StringRef, |
230 | bool, OptionalFileEntryRef, |
231 | SrcMgr::CharacteristicKind) { |
232 | parseToLocation(Loc); |
233 | } |
234 | void 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 | } |
240 | void ExpandModularHeadersPPCallbacks::PragmaWarning(SourceLocation Loc, |
241 | PragmaWarningSpecifier, |
242 | ArrayRef<int>) { |
243 | parseToLocation(Loc); |
244 | } |
245 | void ExpandModularHeadersPPCallbacks::PragmaWarningPush(SourceLocation Loc, |
246 | int) { |
247 | parseToLocation(Loc); |
248 | } |
249 | void ExpandModularHeadersPPCallbacks::PragmaWarningPop(SourceLocation Loc) { |
250 | parseToLocation(Loc); |
251 | } |
252 | void ExpandModularHeadersPPCallbacks::PragmaAssumeNonNullBegin( |
253 | SourceLocation Loc) { |
254 | parseToLocation(Loc); |
255 | } |
256 | void ExpandModularHeadersPPCallbacks::PragmaAssumeNonNullEnd( |
257 | SourceLocation Loc) { |
258 | parseToLocation(Loc); |
259 | } |
260 | void 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 | } |
267 | void ExpandModularHeadersPPCallbacks::MacroDefined(const Token &MacroNameTok, |
268 | const MacroDirective *MD) { |
269 | parseToLocation(Loc: MD->getLocation()); |
270 | } |
271 | void ExpandModularHeadersPPCallbacks::MacroUndefined( |
272 | const Token &, const MacroDefinition &, const MacroDirective *Undef) { |
273 | if (Undef) |
274 | parseToLocation(Loc: Undef->getLocation()); |
275 | } |
276 | void 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 | } |
282 | void 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 | } |
287 | void ExpandModularHeadersPPCallbacks::If(SourceLocation Loc, SourceRange, |
288 | ConditionValueKind) { |
289 | parseToLocation(Loc); |
290 | } |
291 | void ExpandModularHeadersPPCallbacks::Elif(SourceLocation Loc, SourceRange, |
292 | ConditionValueKind, SourceLocation) { |
293 | parseToLocation(Loc); |
294 | } |
295 | void ExpandModularHeadersPPCallbacks::Ifdef(SourceLocation Loc, const Token &, |
296 | const MacroDefinition &) { |
297 | parseToLocation(Loc); |
298 | } |
299 | void ExpandModularHeadersPPCallbacks::Ifndef(SourceLocation Loc, const Token &, |
300 | const MacroDefinition &) { |
301 | parseToLocation(Loc); |
302 | } |
303 | void ExpandModularHeadersPPCallbacks::Else(SourceLocation Loc, SourceLocation) { |
304 | parseToLocation(Loc); |
305 | } |
306 | void ExpandModularHeadersPPCallbacks::Endif(SourceLocation Loc, |
307 | SourceLocation) { |
308 | parseToLocation(Loc); |
309 | } |
310 | |
311 | } // namespace clang::tooling |
312 | |