/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===- ExtractAPI/ExtractAPIConsumer.cpp ------------------------*- 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 | | /// \file |
10 | | /// This file implements the ExtractAPIAction, and ASTConsumer to collect API |
11 | | /// information. |
12 | | /// |
13 | | //===----------------------------------------------------------------------===// |
14 | | |
15 | | #include "clang/AST/ASTConcept.h" |
16 | | #include "clang/AST/ASTConsumer.h" |
17 | | #include "clang/AST/ASTContext.h" |
18 | | #include "clang/AST/DeclObjC.h" |
19 | | #include "clang/Basic/DiagnosticFrontend.h" |
20 | | #include "clang/Basic/SourceLocation.h" |
21 | | #include "clang/Basic/SourceManager.h" |
22 | | #include "clang/Basic/TargetInfo.h" |
23 | | #include "clang/ExtractAPI/API.h" |
24 | | #include "clang/ExtractAPI/APIIgnoresList.h" |
25 | | #include "clang/ExtractAPI/ExtractAPIVisitor.h" |
26 | | #include "clang/ExtractAPI/FrontendActions.h" |
27 | | #include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h" |
28 | | #include "clang/Frontend/ASTConsumers.h" |
29 | | #include "clang/Frontend/CompilerInstance.h" |
30 | | #include "clang/Frontend/FrontendOptions.h" |
31 | | #include "clang/Frontend/MultiplexConsumer.h" |
32 | | #include "clang/Lex/MacroInfo.h" |
33 | | #include "clang/Lex/PPCallbacks.h" |
34 | | #include "clang/Lex/Preprocessor.h" |
35 | | #include "clang/Lex/PreprocessorOptions.h" |
36 | | #include "llvm/ADT/DenseSet.h" |
37 | | #include "llvm/ADT/STLExtras.h" |
38 | | #include "llvm/ADT/SmallString.h" |
39 | | #include "llvm/ADT/SmallVector.h" |
40 | | #include "llvm/Support/Casting.h" |
41 | | #include "llvm/Support/Error.h" |
42 | | #include "llvm/Support/FileSystem.h" |
43 | | #include "llvm/Support/MemoryBuffer.h" |
44 | | #include "llvm/Support/Path.h" |
45 | | #include "llvm/Support/Regex.h" |
46 | | #include "llvm/Support/raw_ostream.h" |
47 | | #include <memory> |
48 | | #include <optional> |
49 | | #include <utility> |
50 | | |
51 | | using namespace clang; |
52 | | using namespace extractapi; |
53 | | |
54 | | namespace { |
55 | | |
56 | | std::optional<std::string> getRelativeIncludeName(const CompilerInstance &CI, |
57 | | StringRef File, |
58 | 65 | bool *IsQuoted = nullptr) { |
59 | 65 | assert(CI.hasFileManager() && |
60 | 65 | "CompilerInstance does not have a FileNamager!"); |
61 | | |
62 | 65 | using namespace llvm::sys; |
63 | | // Matches framework include patterns |
64 | 65 | const llvm::Regex Rule("/(.+)\\.framework/(.+)?Headers/(.+)"); |
65 | | |
66 | 65 | const auto &FS = CI.getVirtualFileSystem(); |
67 | | |
68 | 65 | SmallString<128> FilePath(File.begin(), File.end()); |
69 | 65 | FS.makeAbsolute(FilePath); |
70 | 65 | path::remove_dots(FilePath, true); |
71 | 65 | FilePath = path::convert_to_slash(FilePath); |
72 | 65 | File = FilePath; |
73 | | |
74 | | // Checks whether `Dir` is a strict path prefix of `File`. If so returns |
75 | | // the prefix length. Otherwise return 0. |
76 | 114 | auto CheckDir = [&](llvm::StringRef Dir) -> unsigned { |
77 | 114 | llvm::SmallString<32> DirPath(Dir.begin(), Dir.end()); |
78 | 114 | FS.makeAbsolute(DirPath); |
79 | 114 | path::remove_dots(DirPath, true); |
80 | 114 | Dir = DirPath; |
81 | 114 | for (auto NI = path::begin(File), NE = path::end(File), |
82 | 114 | DI = path::begin(Dir), DE = path::end(Dir); |
83 | 667 | /*termination condition in loop*/; ++NI, ++DI553 ) { |
84 | | // '.' components in File are ignored. |
85 | 667 | while (NI != NE && *NI == ".") |
86 | 0 | ++NI; |
87 | 667 | if (NI == NE) |
88 | 0 | break; |
89 | | |
90 | | // '.' components in Dir are ignored. |
91 | 667 | while (DI != DE && *DI == "."661 ) |
92 | 0 | ++DI; |
93 | | |
94 | | // Dir is a prefix of File, up to '.' components and choice of path |
95 | | // separators. |
96 | 667 | if (DI == DE) |
97 | 6 | return NI - path::begin(File); |
98 | | |
99 | | // Consider all path separators equal. |
100 | 661 | if (NI->size() == 1 && DI->size() == 1114 && |
101 | 661 | path::is_separator(NI->front())114 && path::is_separator(DI->front())114 ) |
102 | 114 | continue; |
103 | | |
104 | | // Special case Apple .sdk folders since the search path is typically a |
105 | | // symlink like `iPhoneSimulator14.5.sdk` while the file is instead |
106 | | // located in `iPhoneSimulator.sdk` (the real folder). |
107 | 547 | if (NI->endswith(".sdk") && DI->endswith(".sdk")0 ) { |
108 | 0 | StringRef NBasename = path::stem(*NI); |
109 | 0 | StringRef DBasename = path::stem(*DI); |
110 | 0 | if (DBasename.startswith(NBasename)) |
111 | 0 | continue; |
112 | 0 | } |
113 | | |
114 | 547 | if (*NI != *DI) |
115 | 108 | break; |
116 | 547 | } |
117 | 108 | return 0; |
118 | 114 | }; |
119 | | |
120 | 65 | unsigned PrefixLength = 0; |
121 | | |
122 | | // Go through the search paths and find the first one that is a prefix of |
123 | | // the header. |
124 | 120 | for (const auto &Entry : CI.getHeaderSearchOpts().UserEntries) { |
125 | | // Note whether the match is found in a quoted entry. |
126 | 120 | if (IsQuoted) |
127 | 120 | *IsQuoted = Entry.Group == frontend::Quoted; |
128 | | |
129 | 120 | if (auto EntryFile = CI.getFileManager().getOptionalFileRef(Entry.Path)) { |
130 | 6 | if (auto HMap = HeaderMap::Create(*EntryFile, CI.getFileManager())) { |
131 | | // If this is a headermap entry, try to reverse lookup the full path |
132 | | // for a spelled name before mapping. |
133 | 6 | StringRef SpelledFilename = HMap->reverseLookupFilename(File); |
134 | 6 | if (!SpelledFilename.empty()) |
135 | 1 | return SpelledFilename.str(); |
136 | | |
137 | | // No matching mapping in this headermap, try next search entry. |
138 | 5 | continue; |
139 | 6 | } |
140 | 6 | } |
141 | | |
142 | | // Entry is a directory search entry, try to check if it's a prefix of File. |
143 | 114 | PrefixLength = CheckDir(Entry.Path); |
144 | 114 | if (PrefixLength > 0) { |
145 | | // The header is found in a framework path, construct the framework-style |
146 | | // include name `<Framework/Header.h>` |
147 | 6 | if (Entry.IsFramework) { |
148 | 3 | SmallVector<StringRef, 4> Matches; |
149 | 3 | Rule.match(File, &Matches); |
150 | | // Returned matches are always in stable order. |
151 | 3 | if (Matches.size() != 4) |
152 | 0 | return std::nullopt; |
153 | | |
154 | 3 | return path::convert_to_slash( |
155 | 3 | (Matches[1].drop_front(Matches[1].rfind('/') + 1) + "/" + |
156 | 3 | Matches[3]) |
157 | 3 | .str()); |
158 | 3 | } |
159 | | |
160 | | // The header is found in a normal search path, strip the search path |
161 | | // prefix to get an include name. |
162 | 3 | return path::convert_to_slash(File.drop_front(PrefixLength)); |
163 | 6 | } |
164 | 114 | } |
165 | | |
166 | | // Couldn't determine a include name, use full path instead. |
167 | 58 | return std::nullopt; |
168 | 65 | } |
169 | | |
170 | | struct LocationFileChecker { |
171 | 162 | bool operator()(SourceLocation Loc) { |
172 | | // If the loc refers to a macro expansion we need to first get the file |
173 | | // location of the expansion. |
174 | 162 | auto &SM = CI.getSourceManager(); |
175 | 162 | auto FileLoc = SM.getFileLoc(Loc); |
176 | 162 | FileID FID = SM.getFileID(FileLoc); |
177 | 162 | if (FID.isInvalid()) |
178 | 0 | return false; |
179 | | |
180 | 162 | OptionalFileEntryRef File = SM.getFileEntryRefForID(FID); |
181 | 162 | if (!File) |
182 | 0 | return false; |
183 | | |
184 | 162 | if (KnownFileEntries.count(*File)) |
185 | 149 | return true; |
186 | | |
187 | 13 | if (ExternalFileEntries.count(*File)) |
188 | 6 | return false; |
189 | | |
190 | 7 | StringRef FileName = SM.getFileManager().getCanonicalName(*File); |
191 | | |
192 | | // Try to reduce the include name the same way we tried to include it. |
193 | 7 | bool IsQuoted = false; |
194 | 7 | if (auto IncludeName = getRelativeIncludeName(CI, FileName, &IsQuoted)) |
195 | 4 | if (llvm::any_of(KnownFiles, |
196 | 9 | [&IsQuoted, &IncludeName](const auto &KnownFile) { |
197 | 9 | return KnownFile.first.equals(*IncludeName) && |
198 | 9 | KnownFile.second == IsQuoted2 ; |
199 | 9 | })) { |
200 | 2 | KnownFileEntries.insert(*File); |
201 | 2 | return true; |
202 | 2 | } |
203 | | |
204 | | // Record that the file was not found to avoid future reverse lookup for |
205 | | // the same file. |
206 | 5 | ExternalFileEntries.insert(*File); |
207 | 5 | return false; |
208 | 7 | } |
209 | | |
210 | | LocationFileChecker(const CompilerInstance &CI, |
211 | | SmallVector<std::pair<SmallString<32>, bool>> &KnownFiles) |
212 | 53 | : CI(CI), KnownFiles(KnownFiles), ExternalFileEntries() { |
213 | 53 | for (const auto &KnownFile : KnownFiles) |
214 | 58 | if (auto FileEntry = CI.getFileManager().getFile(KnownFile.first)) |
215 | 55 | KnownFileEntries.insert(*FileEntry); |
216 | 53 | } |
217 | | |
218 | | private: |
219 | | const CompilerInstance &CI; |
220 | | SmallVector<std::pair<SmallString<32>, bool>> &KnownFiles; |
221 | | llvm::DenseSet<const FileEntry *> KnownFileEntries; |
222 | | llvm::DenseSet<const FileEntry *> ExternalFileEntries; |
223 | | }; |
224 | | |
225 | | struct BatchExtractAPIVisitor : ExtractAPIVisitor<BatchExtractAPIVisitor> { |
226 | 142 | bool shouldDeclBeIncluded(const Decl *D) const { |
227 | 142 | bool ShouldBeIncluded = true; |
228 | | // Check that we have the definition for redeclarable types. |
229 | 142 | if (auto *TD = llvm::dyn_cast<TagDecl>(D)) |
230 | 39 | ShouldBeIncluded = TD->isThisDeclarationADefinition(); |
231 | 103 | else if (auto *Interface = llvm::dyn_cast<ObjCInterfaceDecl>(D)) |
232 | 9 | ShouldBeIncluded = Interface->isThisDeclarationADefinition(); |
233 | 94 | else if (auto *Protocol = llvm::dyn_cast<ObjCProtocolDecl>(D)) |
234 | 6 | ShouldBeIncluded = Protocol->isThisDeclarationADefinition(); |
235 | | |
236 | 142 | ShouldBeIncluded = ShouldBeIncluded && LCF(D->getLocation())139 ; |
237 | 142 | return ShouldBeIncluded; |
238 | 142 | } |
239 | | |
240 | | BatchExtractAPIVisitor(LocationFileChecker &LCF, ASTContext &Context, |
241 | | APISet &API) |
242 | 53 | : ExtractAPIVisitor<BatchExtractAPIVisitor>(Context, API), LCF(LCF) {} |
243 | | |
244 | | private: |
245 | | LocationFileChecker &LCF; |
246 | | }; |
247 | | |
248 | | class WrappingExtractAPIConsumer : public ASTConsumer { |
249 | | public: |
250 | | WrappingExtractAPIConsumer(ASTContext &Context, APISet &API) |
251 | 3 | : Visitor(Context, API) {} |
252 | | |
253 | 3 | void HandleTranslationUnit(ASTContext &Context) override { |
254 | | // Use ExtractAPIVisitor to traverse symbol declarations in the context. |
255 | 3 | Visitor.TraverseDecl(Context.getTranslationUnitDecl()); |
256 | 3 | } |
257 | | |
258 | | private: |
259 | | ExtractAPIVisitor<> Visitor; |
260 | | }; |
261 | | |
262 | | class ExtractAPIConsumer : public ASTConsumer { |
263 | | public: |
264 | | ExtractAPIConsumer(ASTContext &Context, |
265 | | std::unique_ptr<LocationFileChecker> LCF, APISet &API) |
266 | 53 | : Visitor(*LCF, Context, API), LCF(std::move(LCF)) {} |
267 | | |
268 | 53 | void HandleTranslationUnit(ASTContext &Context) override { |
269 | | // Use ExtractAPIVisitor to traverse symbol declarations in the context. |
270 | 53 | Visitor.TraverseDecl(Context.getTranslationUnitDecl()); |
271 | 53 | } |
272 | | |
273 | | private: |
274 | | BatchExtractAPIVisitor Visitor; |
275 | | std::unique_ptr<LocationFileChecker> LCF; |
276 | | }; |
277 | | |
278 | | class MacroCallback : public PPCallbacks { |
279 | | public: |
280 | | MacroCallback(const SourceManager &SM, APISet &API, Preprocessor &PP) |
281 | 56 | : SM(SM), API(API), PP(PP) {} |
282 | | |
283 | | void MacroDefined(const Token &MacroNameToken, |
284 | 23.4k | const MacroDirective *MD) override { |
285 | 23.4k | auto *MacroInfo = MD->getMacroInfo(); |
286 | | |
287 | 23.4k | if (MacroInfo->isBuiltinMacro()) |
288 | 0 | return; |
289 | | |
290 | 23.4k | auto SourceLoc = MacroNameToken.getLocation(); |
291 | 23.4k | if (SM.isWrittenInBuiltinFile(SourceLoc) || |
292 | 23.4k | SM.isWrittenInCommandLineFile(SourceLoc)52 ) |
293 | 23.4k | return; |
294 | | |
295 | 33 | PendingMacros.emplace_back(MacroNameToken, MD); |
296 | 33 | } |
297 | | |
298 | | // If a macro gets undefined at some point during preprocessing of the inputs |
299 | | // it means that it isn't an exposed API and we should therefore not add a |
300 | | // macro definition for it. |
301 | | void MacroUndefined(const Token &MacroNameToken, const MacroDefinition &MD, |
302 | 2 | const MacroDirective *Undef) override { |
303 | | // If this macro wasn't previously defined we don't need to do anything |
304 | | // here. |
305 | 2 | if (!Undef) |
306 | 1 | return; |
307 | | |
308 | 2 | llvm::erase_if(PendingMacros, [&MD, this](const PendingMacro &PM) 1 { |
309 | 2 | return MD.getMacroInfo()->isIdenticalTo(*PM.MD->getMacroInfo(), PP, |
310 | 2 | /*Syntactically*/ false); |
311 | 2 | }); |
312 | 1 | } |
313 | | |
314 | 56 | void EndOfMainFile() override { |
315 | 56 | for (auto &PM : PendingMacros) { |
316 | | // `isUsedForHeaderGuard` is only set when the preprocessor leaves the |
317 | | // file so check for it here. |
318 | 32 | if (PM.MD->getMacroInfo()->isUsedForHeaderGuard()) |
319 | 3 | continue; |
320 | | |
321 | 29 | if (!shouldMacroBeIncluded(PM)) |
322 | 5 | continue; |
323 | | |
324 | 24 | StringRef Name = PM.MacroNameToken.getIdentifierInfo()->getName(); |
325 | 24 | PresumedLoc Loc = SM.getPresumedLoc(PM.MacroNameToken.getLocation()); |
326 | 24 | StringRef USR = |
327 | 24 | API.recordUSRForMacro(Name, PM.MacroNameToken.getLocation(), SM); |
328 | | |
329 | 24 | API.addMacroDefinition( |
330 | 24 | Name, USR, Loc, |
331 | 24 | DeclarationFragmentsBuilder::getFragmentsForMacro(Name, PM.MD), |
332 | 24 | DeclarationFragmentsBuilder::getSubHeadingForMacro(Name), |
333 | 24 | SM.isInSystemHeader(PM.MacroNameToken.getLocation())); |
334 | 24 | } |
335 | | |
336 | 56 | PendingMacros.clear(); |
337 | 56 | } |
338 | | |
339 | | protected: |
340 | | struct PendingMacro { |
341 | | Token MacroNameToken; |
342 | | const MacroDirective *MD; |
343 | | |
344 | | PendingMacro(const Token &MacroNameToken, const MacroDirective *MD) |
345 | 33 | : MacroNameToken(MacroNameToken), MD(MD) {} |
346 | | }; |
347 | | |
348 | 6 | virtual bool shouldMacroBeIncluded(const PendingMacro &PM) { return true; } |
349 | | |
350 | | const SourceManager &SM; |
351 | | APISet &API; |
352 | | Preprocessor &PP; |
353 | | llvm::SmallVector<PendingMacro> PendingMacros; |
354 | | }; |
355 | | |
356 | | class APIMacroCallback : public MacroCallback { |
357 | | public: |
358 | | APIMacroCallback(const SourceManager &SM, APISet &API, Preprocessor &PP, |
359 | | LocationFileChecker &LCF) |
360 | 53 | : MacroCallback(SM, API, PP), LCF(LCF) {} |
361 | | |
362 | 23 | bool shouldMacroBeIncluded(const PendingMacro &PM) override { |
363 | | // Do not include macros from external files |
364 | 23 | return LCF(PM.MacroNameToken.getLocation()); |
365 | 23 | } |
366 | | |
367 | | private: |
368 | | LocationFileChecker &LCF; |
369 | | }; |
370 | | |
371 | | } // namespace |
372 | | |
373 | 56 | void ExtractAPIActionBase::ImplEndSourceFileAction() { |
374 | 56 | if (!OS) |
375 | 0 | return; |
376 | | |
377 | | // Setup a SymbolGraphSerializer to write out collected API information in |
378 | | // the Symbol Graph format. |
379 | | // FIXME: Make the kind of APISerializer configurable. |
380 | 56 | SymbolGraphSerializer SGSerializer(*API, IgnoresList); |
381 | 56 | SGSerializer.serialize(*OS); |
382 | 56 | OS.reset(); |
383 | 56 | } |
384 | | |
385 | | std::unique_ptr<raw_pwrite_stream> |
386 | 53 | ExtractAPIAction::CreateOutputFile(CompilerInstance &CI, StringRef InFile) { |
387 | 53 | std::unique_ptr<raw_pwrite_stream> OS; |
388 | 53 | OS = CI.createDefaultOutputFile(/*Binary=*/false, InFile, |
389 | 53 | /*Extension=*/"json", |
390 | 53 | /*RemoveFileOnSignal=*/false); |
391 | 53 | if (!OS) |
392 | 0 | return nullptr; |
393 | 53 | return OS; |
394 | 53 | } |
395 | | |
396 | | std::unique_ptr<ASTConsumer> |
397 | 53 | ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { |
398 | 53 | OS = CreateOutputFile(CI, InFile); |
399 | | |
400 | 53 | if (!OS) |
401 | 0 | return nullptr; |
402 | | |
403 | 53 | auto ProductName = CI.getFrontendOpts().ProductName; |
404 | | |
405 | | // Now that we have enough information about the language options and the |
406 | | // target triple, let's create the APISet before anyone uses it. |
407 | 53 | API = std::make_unique<APISet>( |
408 | 53 | CI.getTarget().getTriple(), |
409 | 53 | CI.getFrontendOpts().Inputs.back().getKind().getLanguage(), ProductName); |
410 | | |
411 | 53 | auto LCF = std::make_unique<LocationFileChecker>(CI, KnownInputFiles); |
412 | | |
413 | 53 | CI.getPreprocessor().addPPCallbacks(std::make_unique<APIMacroCallback>( |
414 | 53 | CI.getSourceManager(), *API, CI.getPreprocessor(), *LCF)); |
415 | | |
416 | | // Do not include location in anonymous decls. |
417 | 53 | PrintingPolicy Policy = CI.getASTContext().getPrintingPolicy(); |
418 | 53 | Policy.AnonymousTagLocations = false; |
419 | 53 | CI.getASTContext().setPrintingPolicy(Policy); |
420 | | |
421 | 53 | if (!CI.getFrontendOpts().ExtractAPIIgnoresFileList.empty()) { |
422 | 3 | llvm::handleAllErrors( |
423 | 3 | APIIgnoresList::create(CI.getFrontendOpts().ExtractAPIIgnoresFileList, |
424 | 3 | CI.getFileManager()) |
425 | 3 | .moveInto(IgnoresList), |
426 | 3 | [&CI](const IgnoresFileNotFound &Err) { |
427 | 1 | CI.getDiagnostics().Report( |
428 | 1 | diag::err_extract_api_ignores_file_not_found) |
429 | 1 | << Err.Path; |
430 | 1 | }); |
431 | 3 | } |
432 | | |
433 | 53 | return std::make_unique<ExtractAPIConsumer>(CI.getASTContext(), |
434 | 53 | std::move(LCF), *API); |
435 | 53 | } |
436 | | |
437 | 53 | bool ExtractAPIAction::PrepareToExecuteAction(CompilerInstance &CI) { |
438 | 53 | auto &Inputs = CI.getFrontendOpts().Inputs; |
439 | 53 | if (Inputs.empty()) |
440 | 0 | return true; |
441 | | |
442 | 53 | if (!CI.hasFileManager()) |
443 | 53 | if (!CI.createFileManager()) |
444 | 0 | return false; |
445 | | |
446 | 53 | auto Kind = Inputs[0].getKind(); |
447 | | |
448 | | // Convert the header file inputs into a single input buffer. |
449 | 53 | SmallString<256> HeaderContents; |
450 | 53 | bool IsQuoted = false; |
451 | 58 | for (const FrontendInputFile &FIF : Inputs) { |
452 | 58 | if (Kind.isObjectiveC()) |
453 | 17 | HeaderContents += "#import"; |
454 | 41 | else |
455 | 41 | HeaderContents += "#include"; |
456 | | |
457 | 58 | StringRef FilePath = FIF.getFile(); |
458 | 58 | if (auto RelativeName = getRelativeIncludeName(CI, FilePath, &IsQuoted)) { |
459 | 3 | if (IsQuoted) |
460 | 1 | HeaderContents += " \""; |
461 | 2 | else |
462 | 2 | HeaderContents += " <"; |
463 | | |
464 | 3 | HeaderContents += *RelativeName; |
465 | | |
466 | 3 | if (IsQuoted) |
467 | 1 | HeaderContents += "\"\n"; |
468 | 2 | else |
469 | 2 | HeaderContents += ">\n"; |
470 | 3 | KnownInputFiles.emplace_back(static_cast<SmallString<32>>(*RelativeName), |
471 | 3 | IsQuoted); |
472 | 55 | } else { |
473 | 55 | HeaderContents += " \""; |
474 | 55 | HeaderContents += FilePath; |
475 | 55 | HeaderContents += "\"\n"; |
476 | 55 | KnownInputFiles.emplace_back(FilePath, true); |
477 | 55 | } |
478 | 58 | } |
479 | | |
480 | 53 | if (CI.getHeaderSearchOpts().Verbose) |
481 | 1 | CI.getVerboseOutputStream() << getInputBufferName() << ":\n" |
482 | 1 | << HeaderContents << "\n"; |
483 | | |
484 | 53 | Buffer = llvm::MemoryBuffer::getMemBufferCopy(HeaderContents, |
485 | 53 | getInputBufferName()); |
486 | | |
487 | | // Set that buffer up as our "real" input in the CompilerInstance. |
488 | 53 | Inputs.clear(); |
489 | 53 | Inputs.emplace_back(Buffer->getMemBufferRef(), Kind, /*IsSystem*/ false); |
490 | | |
491 | 53 | return true; |
492 | 53 | } |
493 | | |
494 | 53 | void ExtractAPIAction::EndSourceFileAction() { ImplEndSourceFileAction(); } |
495 | | |
496 | | std::unique_ptr<ASTConsumer> |
497 | | WrappingExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, |
498 | 3 | StringRef InFile) { |
499 | 3 | auto OtherConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile); |
500 | 3 | if (!OtherConsumer) |
501 | 0 | return nullptr; |
502 | | |
503 | 3 | CreatedASTConsumer = true; |
504 | | |
505 | 3 | OS = CreateOutputFile(CI, InFile); |
506 | 3 | if (!OS) |
507 | 0 | return nullptr; |
508 | | |
509 | 3 | auto ProductName = CI.getFrontendOpts().ProductName; |
510 | | |
511 | | // Now that we have enough information about the language options and the |
512 | | // target triple, let's create the APISet before anyone uses it. |
513 | 3 | API = std::make_unique<APISet>( |
514 | 3 | CI.getTarget().getTriple(), |
515 | 3 | CI.getFrontendOpts().Inputs.back().getKind().getLanguage(), ProductName); |
516 | | |
517 | 3 | CI.getPreprocessor().addPPCallbacks(std::make_unique<MacroCallback>( |
518 | 3 | CI.getSourceManager(), *API, CI.getPreprocessor())); |
519 | | |
520 | | // Do not include location in anonymous decls. |
521 | 3 | PrintingPolicy Policy = CI.getASTContext().getPrintingPolicy(); |
522 | 3 | Policy.AnonymousTagLocations = false; |
523 | 3 | CI.getASTContext().setPrintingPolicy(Policy); |
524 | | |
525 | 3 | if (!CI.getFrontendOpts().ExtractAPIIgnoresFileList.empty()) { |
526 | 0 | llvm::handleAllErrors( |
527 | 0 | APIIgnoresList::create(CI.getFrontendOpts().ExtractAPIIgnoresFileList, |
528 | 0 | CI.getFileManager()) |
529 | 0 | .moveInto(IgnoresList), |
530 | 0 | [&CI](const IgnoresFileNotFound &Err) { |
531 | 0 | CI.getDiagnostics().Report( |
532 | 0 | diag::err_extract_api_ignores_file_not_found) |
533 | 0 | << Err.Path; |
534 | 0 | }); |
535 | 0 | } |
536 | | |
537 | 3 | auto WrappingConsumer = |
538 | 3 | std::make_unique<WrappingExtractAPIConsumer>(CI.getASTContext(), *API); |
539 | 3 | std::vector<std::unique_ptr<ASTConsumer>> Consumers; |
540 | 3 | Consumers.push_back(std::move(OtherConsumer)); |
541 | 3 | Consumers.push_back(std::move(WrappingConsumer)); |
542 | | |
543 | 3 | return std::make_unique<MultiplexConsumer>(std::move(Consumers)); |
544 | 3 | } |
545 | | |
546 | 3 | void WrappingExtractAPIAction::EndSourceFileAction() { |
547 | | // Invoke wrapped action's method. |
548 | 3 | WrapperFrontendAction::EndSourceFileAction(); |
549 | | |
550 | 3 | if (CreatedASTConsumer) { |
551 | 3 | ImplEndSourceFileAction(); |
552 | 3 | } |
553 | 3 | } |
554 | | |
555 | | std::unique_ptr<raw_pwrite_stream> |
556 | | WrappingExtractAPIAction::CreateOutputFile(CompilerInstance &CI, |
557 | 3 | StringRef InFile) { |
558 | 3 | std::unique_ptr<raw_pwrite_stream> OS; |
559 | 3 | std::string OutputDir = CI.getFrontendOpts().SymbolGraphOutputDir; |
560 | | |
561 | | // The symbol graphs need to be generated as a side effect of regular |
562 | | // compilation so the output should be dumped in the directory provided with |
563 | | // the command line option. |
564 | 3 | llvm::SmallString<128> OutFilePath(OutputDir); |
565 | 3 | auto Seperator = llvm::sys::path::get_separator(); |
566 | 3 | auto Infilename = llvm::sys::path::filename(InFile); |
567 | 3 | OutFilePath.append({Seperator, Infilename}); |
568 | 3 | llvm::sys::path::replace_extension(OutFilePath, "json"); |
569 | | // StringRef outputFilePathref = *OutFilePath; |
570 | | |
571 | | // don't use the default output file |
572 | 3 | OS = CI.createOutputFile(/*OutputPath=*/OutFilePath, /*Binary=*/false, |
573 | 3 | /*RemoveFileOnSignal=*/true, |
574 | 3 | /*UseTemporary=*/true, |
575 | 3 | /*CreateMissingDirectories=*/true); |
576 | 3 | if (!OS) |
577 | 0 | return nullptr; |
578 | 3 | return OS; |
579 | 3 | } |