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. |
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 | |
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, 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 | |
105 | ExpandModularHeadersPPCallbacks::~ExpandModularHeadersPPCallbacks() = default; |
106 | |
107 | Preprocessor *ExpandModularHeadersPPCallbacks::getPreprocessor() const { |
108 | return PP.get(); |
109 | } |
110 | |
111 | void 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 | |
131 | void 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 | |
152 | void 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 | |
161 | void 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 | |
176 | void 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. |
184 | void ExpandModularHeadersPPCallbacks::Ident(SourceLocation Loc, StringRef) { |
185 | parseToLocation(Loc); |
186 | } |
187 | void ExpandModularHeadersPPCallbacks::PragmaDirective(SourceLocation Loc, |
188 | PragmaIntroducerKind) { |
189 | parseToLocation(Loc); |
190 | } |
191 | void ExpandModularHeadersPPCallbacks::PragmaComment(SourceLocation Loc, |
192 | const IdentifierInfo *, |
193 | StringRef) { |
194 | parseToLocation(Loc); |
195 | } |
196 | void ExpandModularHeadersPPCallbacks::PragmaDetectMismatch(SourceLocation Loc, |
197 | StringRef, |
198 | StringRef) { |
199 | parseToLocation(Loc); |
200 | } |
201 | void ExpandModularHeadersPPCallbacks::PragmaDebug(SourceLocation Loc, |
202 | StringRef) { |
203 | parseToLocation(Loc); |
204 | } |
205 | void ExpandModularHeadersPPCallbacks::PragmaMessage(SourceLocation Loc, |
206 | StringRef, |
207 | PragmaMessageKind, |
208 | StringRef) { |
209 | parseToLocation(Loc); |
210 | } |
211 | void ExpandModularHeadersPPCallbacks::PragmaDiagnosticPush(SourceLocation Loc, |
212 | StringRef) { |
213 | parseToLocation(Loc); |
214 | } |
215 | void ExpandModularHeadersPPCallbacks::PragmaDiagnosticPop(SourceLocation Loc, |
216 | StringRef) { |
217 | parseToLocation(Loc); |
218 | } |
219 | void ExpandModularHeadersPPCallbacks::PragmaDiagnostic(SourceLocation Loc, |
220 | StringRef, |
221 | diag::Severity, |
222 | StringRef) { |
223 | parseToLocation(Loc); |
224 | } |
225 | void ExpandModularHeadersPPCallbacks::HasInclude(SourceLocation Loc, StringRef, |
226 | bool, OptionalFileEntryRef, |
227 | SrcMgr::CharacteristicKind) { |
228 | parseToLocation(Loc); |
229 | } |
230 | void 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 | } |
236 | void ExpandModularHeadersPPCallbacks::PragmaWarning(SourceLocation Loc, |
237 | PragmaWarningSpecifier, |
238 | ArrayRef<int>) { |
239 | parseToLocation(Loc); |
240 | } |
241 | void ExpandModularHeadersPPCallbacks::PragmaWarningPush(SourceLocation Loc, |
242 | int) { |
243 | parseToLocation(Loc); |
244 | } |
245 | void ExpandModularHeadersPPCallbacks::PragmaWarningPop(SourceLocation Loc) { |
246 | parseToLocation(Loc); |
247 | } |
248 | void ExpandModularHeadersPPCallbacks::PragmaAssumeNonNullBegin( |
249 | SourceLocation Loc) { |
250 | parseToLocation(Loc); |
251 | } |
252 | void ExpandModularHeadersPPCallbacks::PragmaAssumeNonNullEnd( |
253 | SourceLocation Loc) { |
254 | parseToLocation(Loc); |
255 | } |
256 | void 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 | } |
263 | void ExpandModularHeadersPPCallbacks::MacroDefined(const Token &MacroNameTok, |
264 | const MacroDirective *MD) { |
265 | parseToLocation(Loc: MD->getLocation()); |
266 | } |
267 | void ExpandModularHeadersPPCallbacks::MacroUndefined( |
268 | const Token &, const MacroDefinition &, const MacroDirective *Undef) { |
269 | if (Undef) |
270 | parseToLocation(Loc: Undef->getLocation()); |
271 | } |
272 | void 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 | } |
278 | void 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 | } |
283 | void ExpandModularHeadersPPCallbacks::If(SourceLocation Loc, SourceRange, |
284 | ConditionValueKind) { |
285 | parseToLocation(Loc); |
286 | } |
287 | void ExpandModularHeadersPPCallbacks::Elif(SourceLocation Loc, SourceRange, |
288 | ConditionValueKind, SourceLocation) { |
289 | parseToLocation(Loc); |
290 | } |
291 | void ExpandModularHeadersPPCallbacks::Ifdef(SourceLocation Loc, const Token &, |
292 | const MacroDefinition &) { |
293 | parseToLocation(Loc); |
294 | } |
295 | void ExpandModularHeadersPPCallbacks::Ifndef(SourceLocation Loc, const Token &, |
296 | const MacroDefinition &) { |
297 | parseToLocation(Loc); |
298 | } |
299 | void ExpandModularHeadersPPCallbacks::Else(SourceLocation Loc, SourceLocation) { |
300 | parseToLocation(Loc); |
301 | } |
302 | void ExpandModularHeadersPPCallbacks::Endif(SourceLocation Loc, |
303 | SourceLocation) { |
304 | parseToLocation(Loc); |
305 | } |
306 | |
307 | } // namespace clang::tooling |
308 |
Definitions
- FileRecorder
- addNecessaryFile
- recordFileContent
- checkAllFilesRecorded
- ExpandModularHeadersPPCallbacks
- ~ExpandModularHeadersPPCallbacks
- getPreprocessor
- handleModuleFile
- parseToLocation
- FileChanged
- InclusionDirective
- EndOfMainFile
- Ident
- PragmaDirective
- PragmaComment
- PragmaDetectMismatch
- PragmaDebug
- PragmaMessage
- PragmaDiagnosticPush
- PragmaDiagnosticPop
- PragmaDiagnostic
- HasInclude
- PragmaOpenCLExtension
- PragmaWarning
- PragmaWarningPush
- PragmaWarningPop
- PragmaAssumeNonNullBegin
- PragmaAssumeNonNullEnd
- MacroExpands
- MacroDefined
- MacroUndefined
- Defined
- SourceRangeSkipped
- If
- Elif
- Ifdef
- Ifndef
- Else
Learn to use CMake with our Intro Training
Find out more