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 ASTVisitor/Consumer to
11 /// collect API information.
12 ///
13 //===----------------------------------------------------------------------===//
14
15 #include "TypedefUnderlyingTypeResolver.h"
16 #include "clang/AST/ASTConsumer.h"
17 #include "clang/AST/ASTContext.h"
18 #include "clang/AST/Decl.h"
19 #include "clang/AST/DeclCXX.h"
20 #include "clang/AST/ParentMapContext.h"
21 #include "clang/AST/RawCommentList.h"
22 #include "clang/AST/RecursiveASTVisitor.h"
23 #include "clang/Basic/SourceLocation.h"
24 #include "clang/Basic/SourceManager.h"
25 #include "clang/Basic/TargetInfo.h"
26 #include "clang/ExtractAPI/API.h"
27 #include "clang/ExtractAPI/AvailabilityInfo.h"
28 #include "clang/ExtractAPI/DeclarationFragments.h"
29 #include "clang/ExtractAPI/FrontendActions.h"
30 #include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h"
31 #include "clang/Frontend/ASTConsumers.h"
32 #include "clang/Frontend/CompilerInstance.h"
33 #include "clang/Frontend/FrontendOptions.h"
34 #include "clang/Lex/MacroInfo.h"
35 #include "clang/Lex/PPCallbacks.h"
36 #include "clang/Lex/Preprocessor.h"
37 #include "clang/Lex/PreprocessorOptions.h"
38 #include "llvm/ADT/DenseSet.h"
39 #include "llvm/ADT/STLExtras.h"
40 #include "llvm/ADT/SmallVector.h"
41 #include "llvm/Support/FileSystem.h"
42 #include "llvm/Support/MemoryBuffer.h"
43 #include "llvm/Support/Path.h"
44 #include "llvm/Support/Regex.h"
45 #include "llvm/Support/raw_ostream.h"
46 #include <memory>
47 #include <utility>
48
49 using namespace clang;
50 using namespace extractapi;
51
52 namespace {
53
getTypedefName(const TagDecl * Decl)54 StringRef getTypedefName(const TagDecl *Decl) {
55 if (const auto *TypedefDecl = Decl->getTypedefNameForAnonDecl())
56 return TypedefDecl->getName();
57
58 return {};
59 }
60
getRelativeIncludeName(const CompilerInstance & CI,StringRef File,bool * IsQuoted=nullptr)61 Optional<std::string> getRelativeIncludeName(const CompilerInstance &CI,
62 StringRef File,
63 bool *IsQuoted = nullptr) {
64 assert(CI.hasFileManager() &&
65 "CompilerInstance does not have a FileNamager!");
66
67 using namespace llvm::sys;
68 // Matches framework include patterns
69 const llvm::Regex Rule("/(.+)\\.framework/(.+)?Headers/(.+)");
70
71 const auto &FS = CI.getVirtualFileSystem();
72
73 SmallString<128> FilePath(File.begin(), File.end());
74 FS.makeAbsolute(FilePath);
75 path::remove_dots(FilePath, true);
76 FilePath = path::convert_to_slash(FilePath);
77 File = FilePath;
78
79 // Checks whether `Dir` is a strict path prefix of `File`. If so returns
80 // the prefix length. Otherwise return 0.
81 auto CheckDir = [&](llvm::StringRef Dir) -> unsigned {
82 llvm::SmallString<32> DirPath(Dir.begin(), Dir.end());
83 FS.makeAbsolute(DirPath);
84 path::remove_dots(DirPath, true);
85 Dir = DirPath;
86 for (auto NI = path::begin(File), NE = path::end(File),
87 DI = path::begin(Dir), DE = path::end(Dir);
88 /*termination condition in loop*/; ++NI, ++DI) {
89 // '.' components in File are ignored.
90 while (NI != NE && *NI == ".")
91 ++NI;
92 if (NI == NE)
93 break;
94
95 // '.' components in Dir are ignored.
96 while (DI != DE && *DI == ".")
97 ++DI;
98
99 // Dir is a prefix of File, up to '.' components and choice of path
100 // separators.
101 if (DI == DE)
102 return NI - path::begin(File);
103
104 // Consider all path separators equal.
105 if (NI->size() == 1 && DI->size() == 1 &&
106 path::is_separator(NI->front()) && path::is_separator(DI->front()))
107 continue;
108
109 // Special case Apple .sdk folders since the search path is typically a
110 // symlink like `iPhoneSimulator14.5.sdk` while the file is instead
111 // located in `iPhoneSimulator.sdk` (the real folder).
112 if (NI->endswith(".sdk") && DI->endswith(".sdk")) {
113 StringRef NBasename = path::stem(*NI);
114 StringRef DBasename = path::stem(*DI);
115 if (DBasename.startswith(NBasename))
116 continue;
117 }
118
119 if (*NI != *DI)
120 break;
121 }
122 return 0;
123 };
124
125 unsigned PrefixLength = 0;
126
127 // Go through the search paths and find the first one that is a prefix of
128 // the header.
129 for (const auto &Entry : CI.getHeaderSearchOpts().UserEntries) {
130 // Note whether the match is found in a quoted entry.
131 if (IsQuoted)
132 *IsQuoted = Entry.Group == frontend::Quoted;
133
134 if (auto EntryFile = CI.getFileManager().getOptionalFileRef(Entry.Path)) {
135 if (auto HMap = HeaderMap::Create(*EntryFile, CI.getFileManager())) {
136 // If this is a headermap entry, try to reverse lookup the full path
137 // for a spelled name before mapping.
138 StringRef SpelledFilename = HMap->reverseLookupFilename(File);
139 if (!SpelledFilename.empty())
140 return SpelledFilename.str();
141
142 // No matching mapping in this headermap, try next search entry.
143 continue;
144 }
145 }
146
147 // Entry is a directory search entry, try to check if it's a prefix of File.
148 PrefixLength = CheckDir(Entry.Path);
149 if (PrefixLength > 0) {
150 // The header is found in a framework path, construct the framework-style
151 // include name `<Framework/Header.h>`
152 if (Entry.IsFramework) {
153 SmallVector<StringRef, 4> Matches;
154 Rule.match(File, &Matches);
155 // Returned matches are always in stable order.
156 if (Matches.size() != 4)
157 return None;
158
159 return path::convert_to_slash(
160 (Matches[1].drop_front(Matches[1].rfind('/') + 1) + "/" +
161 Matches[3])
162 .str());
163 }
164
165 // The header is found in a normal search path, strip the search path
166 // prefix to get an include name.
167 return path::convert_to_slash(File.drop_front(PrefixLength));
168 }
169 }
170
171 // Couldn't determine a include name, use full path instead.
172 return None;
173 }
174
175 struct LocationFileChecker {
isLocationInKnownFile__anon4a2c2dcf0111::LocationFileChecker176 bool isLocationInKnownFile(SourceLocation Loc) {
177 // If the loc refers to a macro expansion we need to first get the file
178 // location of the expansion.
179 auto &SM = CI.getSourceManager();
180 auto FileLoc = SM.getFileLoc(Loc);
181 FileID FID = SM.getFileID(FileLoc);
182 if (FID.isInvalid())
183 return false;
184
185 const auto *File = SM.getFileEntryForID(FID);
186 if (!File)
187 return false;
188
189 if (KnownFileEntries.count(File))
190 return true;
191
192 if (ExternalFileEntries.count(File))
193 return false;
194
195 StringRef FileName = File->tryGetRealPathName().empty()
196 ? File->getName()
197 : File->tryGetRealPathName();
198
199 // Try to reduce the include name the same way we tried to include it.
200 bool IsQuoted = false;
201 if (auto IncludeName = getRelativeIncludeName(CI, FileName, &IsQuoted))
202 if (llvm::any_of(KnownFiles,
203 [&IsQuoted, &IncludeName](const auto &KnownFile) {
204 return KnownFile.first.equals(*IncludeName) &&
205 KnownFile.second == IsQuoted;
206 })) {
207 KnownFileEntries.insert(File);
208 return true;
209 }
210
211 // Record that the file was not found to avoid future reverse lookup for
212 // the same file.
213 ExternalFileEntries.insert(File);
214 return false;
215 }
216
LocationFileChecker__anon4a2c2dcf0111::LocationFileChecker217 LocationFileChecker(const CompilerInstance &CI,
218 SmallVector<std::pair<SmallString<32>, bool>> &KnownFiles)
219 : CI(CI), KnownFiles(KnownFiles), ExternalFileEntries() {
220 for (const auto &KnownFile : KnownFiles)
221 if (auto FileEntry = CI.getFileManager().getFile(KnownFile.first))
222 KnownFileEntries.insert(*FileEntry);
223 }
224
225 private:
226 const CompilerInstance &CI;
227 SmallVector<std::pair<SmallString<32>, bool>> &KnownFiles;
228 llvm::DenseSet<const FileEntry *> KnownFileEntries;
229 llvm::DenseSet<const FileEntry *> ExternalFileEntries;
230 };
231
232 /// The RecursiveASTVisitor to traverse symbol declarations and collect API
233 /// information.
234 class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
235 public:
ExtractAPIVisitor(ASTContext & Context,LocationFileChecker & LCF,APISet & API)236 ExtractAPIVisitor(ASTContext &Context, LocationFileChecker &LCF, APISet &API)
237 : Context(Context), API(API), LCF(LCF) {}
238
getAPI() const239 const APISet &getAPI() const { return API; }
240
VisitVarDecl(const VarDecl * Decl)241 bool VisitVarDecl(const VarDecl *Decl) {
242 // Skip function parameters.
243 if (isa<ParmVarDecl>(Decl))
244 return true;
245
246 // Skip non-global variables in records (struct/union/class).
247 if (Decl->getDeclContext()->isRecord())
248 return true;
249
250 // Skip local variables inside function or method.
251 if (!Decl->isDefinedOutsideFunctionOrMethod())
252 return true;
253
254 // If this is a template but not specialization or instantiation, skip.
255 if (Decl->getASTContext().getTemplateOrSpecializationInfo(Decl) &&
256 Decl->getTemplateSpecializationKind() == TSK_Undeclared)
257 return true;
258
259 if (!LCF.isLocationInKnownFile(Decl->getLocation()))
260 return true;
261
262 // Collect symbol information.
263 StringRef Name = Decl->getName();
264 StringRef USR = API.recordUSR(Decl);
265 PresumedLoc Loc =
266 Context.getSourceManager().getPresumedLoc(Decl->getLocation());
267 AvailabilityInfo Availability = getAvailability(Decl);
268 LinkageInfo Linkage = Decl->getLinkageAndVisibility();
269 DocComment Comment;
270 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
271 Comment = RawComment->getFormattedLines(Context.getSourceManager(),
272 Context.getDiagnostics());
273
274 // Build declaration fragments and sub-heading for the variable.
275 DeclarationFragments Declaration =
276 DeclarationFragmentsBuilder::getFragmentsForVar(Decl);
277 DeclarationFragments SubHeading =
278 DeclarationFragmentsBuilder::getSubHeading(Decl);
279
280 // Add the global variable record to the API set.
281 API.addGlobalVar(Name, USR, Loc, Availability, Linkage, Comment,
282 Declaration, SubHeading);
283 return true;
284 }
285
VisitFunctionDecl(const FunctionDecl * Decl)286 bool VisitFunctionDecl(const FunctionDecl *Decl) {
287 if (const auto *Method = dyn_cast<CXXMethodDecl>(Decl)) {
288 // Skip member function in class templates.
289 if (Method->getParent()->getDescribedClassTemplate() != nullptr)
290 return true;
291
292 // Skip methods in records.
293 for (auto P : Context.getParents(*Method)) {
294 if (P.get<CXXRecordDecl>())
295 return true;
296 }
297
298 // Skip ConstructorDecl and DestructorDecl.
299 if (isa<CXXConstructorDecl>(Method) || isa<CXXDestructorDecl>(Method))
300 return true;
301 }
302
303 // Skip templated functions.
304 switch (Decl->getTemplatedKind()) {
305 case FunctionDecl::TK_NonTemplate:
306 case FunctionDecl::TK_DependentNonTemplate:
307 break;
308 case FunctionDecl::TK_MemberSpecialization:
309 case FunctionDecl::TK_FunctionTemplateSpecialization:
310 if (auto *TemplateInfo = Decl->getTemplateSpecializationInfo()) {
311 if (!TemplateInfo->isExplicitInstantiationOrSpecialization())
312 return true;
313 }
314 break;
315 case FunctionDecl::TK_FunctionTemplate:
316 case FunctionDecl::TK_DependentFunctionTemplateSpecialization:
317 return true;
318 }
319
320 if (!LCF.isLocationInKnownFile(Decl->getLocation()))
321 return true;
322
323 // Collect symbol information.
324 StringRef Name = Decl->getName();
325 StringRef USR = API.recordUSR(Decl);
326 PresumedLoc Loc =
327 Context.getSourceManager().getPresumedLoc(Decl->getLocation());
328 AvailabilityInfo Availability = getAvailability(Decl);
329 LinkageInfo Linkage = Decl->getLinkageAndVisibility();
330 DocComment Comment;
331 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
332 Comment = RawComment->getFormattedLines(Context.getSourceManager(),
333 Context.getDiagnostics());
334
335 // Build declaration fragments, sub-heading, and signature of the function.
336 DeclarationFragments Declaration =
337 DeclarationFragmentsBuilder::getFragmentsForFunction(Decl);
338 DeclarationFragments SubHeading =
339 DeclarationFragmentsBuilder::getSubHeading(Decl);
340 FunctionSignature Signature =
341 DeclarationFragmentsBuilder::getFunctionSignature(Decl);
342
343 // Add the function record to the API set.
344 API.addGlobalFunction(Name, USR, Loc, Availability, Linkage, Comment,
345 Declaration, SubHeading, Signature);
346 return true;
347 }
348
VisitEnumDecl(const EnumDecl * Decl)349 bool VisitEnumDecl(const EnumDecl *Decl) {
350 if (!Decl->isComplete())
351 return true;
352
353 // Skip forward declaration.
354 if (!Decl->isThisDeclarationADefinition())
355 return true;
356
357 if (!LCF.isLocationInKnownFile(Decl->getLocation()))
358 return true;
359
360 // Collect symbol information.
361 std::string NameString = Decl->getQualifiedNameAsString();
362 StringRef Name(NameString);
363 if (Name.empty())
364 Name = getTypedefName(Decl);
365
366 StringRef USR = API.recordUSR(Decl);
367 PresumedLoc Loc =
368 Context.getSourceManager().getPresumedLoc(Decl->getLocation());
369 AvailabilityInfo Availability = getAvailability(Decl);
370 DocComment Comment;
371 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
372 Comment = RawComment->getFormattedLines(Context.getSourceManager(),
373 Context.getDiagnostics());
374
375 // Build declaration fragments and sub-heading for the enum.
376 DeclarationFragments Declaration =
377 DeclarationFragmentsBuilder::getFragmentsForEnum(Decl);
378 DeclarationFragments SubHeading =
379 DeclarationFragmentsBuilder::getSubHeading(Decl);
380
381 EnumRecord *EnumRecord =
382 API.addEnum(API.copyString(Name), USR, Loc, Availability, Comment,
383 Declaration, SubHeading);
384
385 // Now collect information about the enumerators in this enum.
386 recordEnumConstants(EnumRecord, Decl->enumerators());
387
388 return true;
389 }
390
VisitRecordDecl(const RecordDecl * Decl)391 bool VisitRecordDecl(const RecordDecl *Decl) {
392 if (!Decl->isCompleteDefinition())
393 return true;
394
395 // Skip C++ structs/classes/unions
396 // TODO: support C++ records
397 if (isa<CXXRecordDecl>(Decl))
398 return true;
399
400 if (!LCF.isLocationInKnownFile(Decl->getLocation()))
401 return true;
402
403 // Collect symbol information.
404 StringRef Name = Decl->getName();
405 if (Name.empty())
406 Name = getTypedefName(Decl);
407 StringRef USR = API.recordUSR(Decl);
408 PresumedLoc Loc =
409 Context.getSourceManager().getPresumedLoc(Decl->getLocation());
410 AvailabilityInfo Availability = getAvailability(Decl);
411 DocComment Comment;
412 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
413 Comment = RawComment->getFormattedLines(Context.getSourceManager(),
414 Context.getDiagnostics());
415
416 // Build declaration fragments and sub-heading for the struct.
417 DeclarationFragments Declaration =
418 DeclarationFragmentsBuilder::getFragmentsForStruct(Decl);
419 DeclarationFragments SubHeading =
420 DeclarationFragmentsBuilder::getSubHeading(Decl);
421
422 StructRecord *StructRecord = API.addStruct(
423 Name, USR, Loc, Availability, Comment, Declaration, SubHeading);
424
425 // Now collect information about the fields in this struct.
426 recordStructFields(StructRecord, Decl->fields());
427
428 return true;
429 }
430
VisitObjCInterfaceDecl(const ObjCInterfaceDecl * Decl)431 bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *Decl) {
432 // Skip forward declaration for classes (@class)
433 if (!Decl->isThisDeclarationADefinition())
434 return true;
435
436 if (!LCF.isLocationInKnownFile(Decl->getLocation()))
437 return true;
438
439 // Collect symbol information.
440 StringRef Name = Decl->getName();
441 StringRef USR = API.recordUSR(Decl);
442 PresumedLoc Loc =
443 Context.getSourceManager().getPresumedLoc(Decl->getLocation());
444 AvailabilityInfo Availability = getAvailability(Decl);
445 LinkageInfo Linkage = Decl->getLinkageAndVisibility();
446 DocComment Comment;
447 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
448 Comment = RawComment->getFormattedLines(Context.getSourceManager(),
449 Context.getDiagnostics());
450
451 // Build declaration fragments and sub-heading for the interface.
452 DeclarationFragments Declaration =
453 DeclarationFragmentsBuilder::getFragmentsForObjCInterface(Decl);
454 DeclarationFragments SubHeading =
455 DeclarationFragmentsBuilder::getSubHeading(Decl);
456
457 // Collect super class information.
458 SymbolReference SuperClass;
459 if (const auto *SuperClassDecl = Decl->getSuperClass()) {
460 SuperClass.Name = SuperClassDecl->getObjCRuntimeNameAsString();
461 SuperClass.USR = API.recordUSR(SuperClassDecl);
462 }
463
464 ObjCInterfaceRecord *ObjCInterfaceRecord =
465 API.addObjCInterface(Name, USR, Loc, Availability, Linkage, Comment,
466 Declaration, SubHeading, SuperClass);
467
468 // Record all methods (selectors). This doesn't include automatically
469 // synthesized property methods.
470 recordObjCMethods(ObjCInterfaceRecord, Decl->methods());
471 recordObjCProperties(ObjCInterfaceRecord, Decl->properties());
472 recordObjCInstanceVariables(ObjCInterfaceRecord, Decl->ivars());
473 recordObjCProtocols(ObjCInterfaceRecord, Decl->protocols());
474
475 return true;
476 }
477
VisitObjCProtocolDecl(const ObjCProtocolDecl * Decl)478 bool VisitObjCProtocolDecl(const ObjCProtocolDecl *Decl) {
479 // Skip forward declaration for protocols (@protocol).
480 if (!Decl->isThisDeclarationADefinition())
481 return true;
482
483 if (!LCF.isLocationInKnownFile(Decl->getLocation()))
484 return true;
485
486 // Collect symbol information.
487 StringRef Name = Decl->getName();
488 StringRef USR = API.recordUSR(Decl);
489 PresumedLoc Loc =
490 Context.getSourceManager().getPresumedLoc(Decl->getLocation());
491 AvailabilityInfo Availability = getAvailability(Decl);
492 DocComment Comment;
493 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
494 Comment = RawComment->getFormattedLines(Context.getSourceManager(),
495 Context.getDiagnostics());
496
497 // Build declaration fragments and sub-heading for the protocol.
498 DeclarationFragments Declaration =
499 DeclarationFragmentsBuilder::getFragmentsForObjCProtocol(Decl);
500 DeclarationFragments SubHeading =
501 DeclarationFragmentsBuilder::getSubHeading(Decl);
502
503 ObjCProtocolRecord *ObjCProtocolRecord = API.addObjCProtocol(
504 Name, USR, Loc, Availability, Comment, Declaration, SubHeading);
505
506 recordObjCMethods(ObjCProtocolRecord, Decl->methods());
507 recordObjCProperties(ObjCProtocolRecord, Decl->properties());
508 recordObjCProtocols(ObjCProtocolRecord, Decl->protocols());
509
510 return true;
511 }
512
VisitTypedefNameDecl(const TypedefNameDecl * Decl)513 bool VisitTypedefNameDecl(const TypedefNameDecl *Decl) {
514 // Skip ObjC Type Parameter for now.
515 if (isa<ObjCTypeParamDecl>(Decl))
516 return true;
517
518 if (!Decl->isDefinedOutsideFunctionOrMethod())
519 return true;
520
521 if (!LCF.isLocationInKnownFile(Decl->getLocation()))
522 return true;
523
524 PresumedLoc Loc =
525 Context.getSourceManager().getPresumedLoc(Decl->getLocation());
526 StringRef Name = Decl->getName();
527 AvailabilityInfo Availability = getAvailability(Decl);
528 StringRef USR = API.recordUSR(Decl);
529 DocComment Comment;
530 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
531 Comment = RawComment->getFormattedLines(Context.getSourceManager(),
532 Context.getDiagnostics());
533
534 QualType Type = Decl->getUnderlyingType();
535 SymbolReference SymRef =
536 TypedefUnderlyingTypeResolver(Context).getSymbolReferenceForType(Type,
537 API);
538
539 API.addTypedef(Name, USR, Loc, Availability, Comment,
540 DeclarationFragmentsBuilder::getFragmentsForTypedef(Decl),
541 DeclarationFragmentsBuilder::getSubHeading(Decl), SymRef);
542
543 return true;
544 }
545
VisitObjCCategoryDecl(const ObjCCategoryDecl * Decl)546 bool VisitObjCCategoryDecl(const ObjCCategoryDecl *Decl) {
547 // Collect symbol information.
548 StringRef Name = Decl->getName();
549 StringRef USR = API.recordUSR(Decl);
550 PresumedLoc Loc =
551 Context.getSourceManager().getPresumedLoc(Decl->getLocation());
552 AvailabilityInfo Availability = getAvailability(Decl);
553 DocComment Comment;
554 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
555 Comment = RawComment->getFormattedLines(Context.getSourceManager(),
556 Context.getDiagnostics());
557 // Build declaration fragments and sub-heading for the category.
558 DeclarationFragments Declaration =
559 DeclarationFragmentsBuilder::getFragmentsForObjCCategory(Decl);
560 DeclarationFragments SubHeading =
561 DeclarationFragmentsBuilder::getSubHeading(Decl);
562
563 const ObjCInterfaceDecl *InterfaceDecl = Decl->getClassInterface();
564 SymbolReference Interface(InterfaceDecl->getName(),
565 API.recordUSR(InterfaceDecl));
566
567 ObjCCategoryRecord *ObjCCategoryRecord =
568 API.addObjCCategory(Name, USR, Loc, Availability, Comment, Declaration,
569 SubHeading, Interface);
570
571 recordObjCMethods(ObjCCategoryRecord, Decl->methods());
572 recordObjCProperties(ObjCCategoryRecord, Decl->properties());
573 recordObjCInstanceVariables(ObjCCategoryRecord, Decl->ivars());
574 recordObjCProtocols(ObjCCategoryRecord, Decl->protocols());
575
576 return true;
577 }
578
579 private:
580 /// Get availability information of the declaration \p D.
getAvailability(const Decl * D) const581 AvailabilityInfo getAvailability(const Decl *D) const {
582 StringRef PlatformName = Context.getTargetInfo().getPlatformName();
583
584 AvailabilityInfo Availability;
585 // Collect availability attributes from all redeclarations.
586 for (const auto *RD : D->redecls()) {
587 for (const auto *A : RD->specific_attrs<AvailabilityAttr>()) {
588 if (A->getPlatform()->getName() != PlatformName)
589 continue;
590 Availability = AvailabilityInfo(A->getIntroduced(), A->getDeprecated(),
591 A->getObsoleted(), A->getUnavailable(),
592 /* UnconditionallyDeprecated */ false,
593 /* UnconditionallyUnavailable */ false);
594 break;
595 }
596
597 if (const auto *A = RD->getAttr<UnavailableAttr>())
598 if (!A->isImplicit()) {
599 Availability.Unavailable = true;
600 Availability.UnconditionallyUnavailable = true;
601 }
602
603 if (const auto *A = RD->getAttr<DeprecatedAttr>())
604 if (!A->isImplicit())
605 Availability.UnconditionallyDeprecated = true;
606 }
607
608 return Availability;
609 }
610
611 /// Collect API information for the enum constants and associate with the
612 /// parent enum.
recordEnumConstants(EnumRecord * EnumRecord,const EnumDecl::enumerator_range Constants)613 void recordEnumConstants(EnumRecord *EnumRecord,
614 const EnumDecl::enumerator_range Constants) {
615 for (const auto *Constant : Constants) {
616 // Collect symbol information.
617 StringRef Name = Constant->getName();
618 StringRef USR = API.recordUSR(Constant);
619 PresumedLoc Loc =
620 Context.getSourceManager().getPresumedLoc(Constant->getLocation());
621 AvailabilityInfo Availability = getAvailability(Constant);
622 DocComment Comment;
623 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Constant))
624 Comment = RawComment->getFormattedLines(Context.getSourceManager(),
625 Context.getDiagnostics());
626
627 // Build declaration fragments and sub-heading for the enum constant.
628 DeclarationFragments Declaration =
629 DeclarationFragmentsBuilder::getFragmentsForEnumConstant(Constant);
630 DeclarationFragments SubHeading =
631 DeclarationFragmentsBuilder::getSubHeading(Constant);
632
633 API.addEnumConstant(EnumRecord, Name, USR, Loc, Availability, Comment,
634 Declaration, SubHeading);
635 }
636 }
637
638 /// Collect API information for the struct fields and associate with the
639 /// parent struct.
recordStructFields(StructRecord * StructRecord,const RecordDecl::field_range Fields)640 void recordStructFields(StructRecord *StructRecord,
641 const RecordDecl::field_range Fields) {
642 for (const auto *Field : Fields) {
643 // Collect symbol information.
644 StringRef Name = Field->getName();
645 StringRef USR = API.recordUSR(Field);
646 PresumedLoc Loc =
647 Context.getSourceManager().getPresumedLoc(Field->getLocation());
648 AvailabilityInfo Availability = getAvailability(Field);
649 DocComment Comment;
650 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Field))
651 Comment = RawComment->getFormattedLines(Context.getSourceManager(),
652 Context.getDiagnostics());
653
654 // Build declaration fragments and sub-heading for the struct field.
655 DeclarationFragments Declaration =
656 DeclarationFragmentsBuilder::getFragmentsForField(Field);
657 DeclarationFragments SubHeading =
658 DeclarationFragmentsBuilder::getSubHeading(Field);
659
660 API.addStructField(StructRecord, Name, USR, Loc, Availability, Comment,
661 Declaration, SubHeading);
662 }
663 }
664
665 /// Collect API information for the Objective-C methods and associate with the
666 /// parent container.
recordObjCMethods(ObjCContainerRecord * Container,const ObjCContainerDecl::method_range Methods)667 void recordObjCMethods(ObjCContainerRecord *Container,
668 const ObjCContainerDecl::method_range Methods) {
669 for (const auto *Method : Methods) {
670 // Don't record selectors for properties.
671 if (Method->isPropertyAccessor())
672 continue;
673
674 StringRef Name = API.copyString(Method->getSelector().getAsString());
675 StringRef USR = API.recordUSR(Method);
676 PresumedLoc Loc =
677 Context.getSourceManager().getPresumedLoc(Method->getLocation());
678 AvailabilityInfo Availability = getAvailability(Method);
679 DocComment Comment;
680 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Method))
681 Comment = RawComment->getFormattedLines(Context.getSourceManager(),
682 Context.getDiagnostics());
683
684 // Build declaration fragments, sub-heading, and signature for the method.
685 DeclarationFragments Declaration =
686 DeclarationFragmentsBuilder::getFragmentsForObjCMethod(Method);
687 DeclarationFragments SubHeading =
688 DeclarationFragmentsBuilder::getSubHeading(Method);
689 FunctionSignature Signature =
690 DeclarationFragmentsBuilder::getFunctionSignature(Method);
691
692 API.addObjCMethod(Container, Name, USR, Loc, Availability, Comment,
693 Declaration, SubHeading, Signature,
694 Method->isInstanceMethod());
695 }
696 }
697
recordObjCProperties(ObjCContainerRecord * Container,const ObjCContainerDecl::prop_range Properties)698 void recordObjCProperties(ObjCContainerRecord *Container,
699 const ObjCContainerDecl::prop_range Properties) {
700 for (const auto *Property : Properties) {
701 StringRef Name = Property->getName();
702 StringRef USR = API.recordUSR(Property);
703 PresumedLoc Loc =
704 Context.getSourceManager().getPresumedLoc(Property->getLocation());
705 AvailabilityInfo Availability = getAvailability(Property);
706 DocComment Comment;
707 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Property))
708 Comment = RawComment->getFormattedLines(Context.getSourceManager(),
709 Context.getDiagnostics());
710
711 // Build declaration fragments and sub-heading for the property.
712 DeclarationFragments Declaration =
713 DeclarationFragmentsBuilder::getFragmentsForObjCProperty(Property);
714 DeclarationFragments SubHeading =
715 DeclarationFragmentsBuilder::getSubHeading(Property);
716
717 StringRef GetterName =
718 API.copyString(Property->getGetterName().getAsString());
719 StringRef SetterName =
720 API.copyString(Property->getSetterName().getAsString());
721
722 // Get the attributes for property.
723 unsigned Attributes = ObjCPropertyRecord::NoAttr;
724 if (Property->getPropertyAttributes() &
725 ObjCPropertyAttribute::kind_readonly)
726 Attributes |= ObjCPropertyRecord::ReadOnly;
727 if (Property->getPropertyAttributes() & ObjCPropertyAttribute::kind_class)
728 Attributes |= ObjCPropertyRecord::Class;
729
730 API.addObjCProperty(
731 Container, Name, USR, Loc, Availability, Comment, Declaration,
732 SubHeading,
733 static_cast<ObjCPropertyRecord::AttributeKind>(Attributes),
734 GetterName, SetterName, Property->isOptional());
735 }
736 }
737
recordObjCInstanceVariables(ObjCContainerRecord * Container,const llvm::iterator_range<DeclContext::specific_decl_iterator<ObjCIvarDecl>> Ivars)738 void recordObjCInstanceVariables(
739 ObjCContainerRecord *Container,
740 const llvm::iterator_range<
741 DeclContext::specific_decl_iterator<ObjCIvarDecl>>
742 Ivars) {
743 for (const auto *Ivar : Ivars) {
744 StringRef Name = Ivar->getName();
745 StringRef USR = API.recordUSR(Ivar);
746 PresumedLoc Loc =
747 Context.getSourceManager().getPresumedLoc(Ivar->getLocation());
748 AvailabilityInfo Availability = getAvailability(Ivar);
749 DocComment Comment;
750 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Ivar))
751 Comment = RawComment->getFormattedLines(Context.getSourceManager(),
752 Context.getDiagnostics());
753
754 // Build declaration fragments and sub-heading for the instance variable.
755 DeclarationFragments Declaration =
756 DeclarationFragmentsBuilder::getFragmentsForField(Ivar);
757 DeclarationFragments SubHeading =
758 DeclarationFragmentsBuilder::getSubHeading(Ivar);
759
760 ObjCInstanceVariableRecord::AccessControl Access =
761 Ivar->getCanonicalAccessControl();
762
763 API.addObjCInstanceVariable(Container, Name, USR, Loc, Availability,
764 Comment, Declaration, SubHeading, Access);
765 }
766 }
767
recordObjCProtocols(ObjCContainerRecord * Container,ObjCInterfaceDecl::protocol_range Protocols)768 void recordObjCProtocols(ObjCContainerRecord *Container,
769 ObjCInterfaceDecl::protocol_range Protocols) {
770 for (const auto *Protocol : Protocols)
771 Container->Protocols.emplace_back(Protocol->getName(),
772 API.recordUSR(Protocol));
773 }
774
775 ASTContext &Context;
776 APISet &API;
777 LocationFileChecker &LCF;
778 };
779
780 class ExtractAPIConsumer : public ASTConsumer {
781 public:
ExtractAPIConsumer(ASTContext & Context,std::unique_ptr<LocationFileChecker> LCF,APISet & API)782 ExtractAPIConsumer(ASTContext &Context,
783 std::unique_ptr<LocationFileChecker> LCF, APISet &API)
784 : Visitor(Context, *LCF, API), LCF(std::move(LCF)) {}
785
HandleTranslationUnit(ASTContext & Context)786 void HandleTranslationUnit(ASTContext &Context) override {
787 // Use ExtractAPIVisitor to traverse symbol declarations in the context.
788 Visitor.TraverseDecl(Context.getTranslationUnitDecl());
789 }
790
791 private:
792 ExtractAPIVisitor Visitor;
793 std::unique_ptr<LocationFileChecker> LCF;
794 };
795
796 class MacroCallback : public PPCallbacks {
797 public:
MacroCallback(const SourceManager & SM,LocationFileChecker & LCF,APISet & API,Preprocessor & PP)798 MacroCallback(const SourceManager &SM, LocationFileChecker &LCF, APISet &API,
799 Preprocessor &PP)
800 : SM(SM), LCF(LCF), API(API), PP(PP) {}
801
MacroDefined(const Token & MacroNameToken,const MacroDirective * MD)802 void MacroDefined(const Token &MacroNameToken,
803 const MacroDirective *MD) override {
804 auto *MacroInfo = MD->getMacroInfo();
805
806 if (MacroInfo->isBuiltinMacro())
807 return;
808
809 auto SourceLoc = MacroNameToken.getLocation();
810 if (SM.isWrittenInBuiltinFile(SourceLoc) ||
811 SM.isWrittenInCommandLineFile(SourceLoc))
812 return;
813
814 PendingMacros.emplace_back(MacroNameToken, MD);
815 }
816
817 // If a macro gets undefined at some point during preprocessing of the inputs
818 // it means that it isn't an exposed API and we should therefore not add a
819 // macro definition for it.
MacroUndefined(const Token & MacroNameToken,const MacroDefinition & MD,const MacroDirective * Undef)820 void MacroUndefined(const Token &MacroNameToken, const MacroDefinition &MD,
821 const MacroDirective *Undef) override {
822 // If this macro wasn't previously defined we don't need to do anything
823 // here.
824 if (!Undef)
825 return;
826
827 llvm::erase_if(PendingMacros, [&MD, this](const PendingMacro &PM) {
828 return MD.getMacroInfo()->isIdenticalTo(*PM.MD->getMacroInfo(), PP,
829 /*Syntactically*/ false);
830 });
831 }
832
EndOfMainFile()833 void EndOfMainFile() override {
834 for (auto &PM : PendingMacros) {
835 // `isUsedForHeaderGuard` is only set when the preprocessor leaves the
836 // file so check for it here.
837 if (PM.MD->getMacroInfo()->isUsedForHeaderGuard())
838 continue;
839
840 if (!LCF.isLocationInKnownFile(PM.MacroNameToken.getLocation()))
841 continue;
842
843 StringRef Name = PM.MacroNameToken.getIdentifierInfo()->getName();
844 PresumedLoc Loc = SM.getPresumedLoc(PM.MacroNameToken.getLocation());
845 StringRef USR =
846 API.recordUSRForMacro(Name, PM.MacroNameToken.getLocation(), SM);
847
848 API.addMacroDefinition(
849 Name, USR, Loc,
850 DeclarationFragmentsBuilder::getFragmentsForMacro(Name, PM.MD),
851 DeclarationFragmentsBuilder::getSubHeadingForMacro(Name));
852 }
853
854 PendingMacros.clear();
855 }
856
857 private:
858 struct PendingMacro {
859 Token MacroNameToken;
860 const MacroDirective *MD;
861
PendingMacro__anon4a2c2dcf0111::MacroCallback::PendingMacro862 PendingMacro(const Token &MacroNameToken, const MacroDirective *MD)
863 : MacroNameToken(MacroNameToken), MD(MD) {}
864 };
865
866 const SourceManager &SM;
867 LocationFileChecker &LCF;
868 APISet &API;
869 Preprocessor &PP;
870 llvm::SmallVector<PendingMacro> PendingMacros;
871 };
872
873 } // namespace
874
875 std::unique_ptr<ASTConsumer>
CreateASTConsumer(CompilerInstance & CI,StringRef InFile)876 ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
877 OS = CreateOutputFile(CI, InFile);
878 if (!OS)
879 return nullptr;
880
881 ProductName = CI.getFrontendOpts().ProductName;
882
883 // Now that we have enough information about the language options and the
884 // target triple, let's create the APISet before anyone uses it.
885 API = std::make_unique<APISet>(
886 CI.getTarget().getTriple(),
887 CI.getFrontendOpts().Inputs.back().getKind().getLanguage());
888
889 auto LCF = std::make_unique<LocationFileChecker>(CI, KnownInputFiles);
890
891 CI.getPreprocessor().addPPCallbacks(std::make_unique<MacroCallback>(
892 CI.getSourceManager(), *LCF, *API, CI.getPreprocessor()));
893
894 return std::make_unique<ExtractAPIConsumer>(CI.getASTContext(),
895 std::move(LCF), *API);
896 }
897
PrepareToExecuteAction(CompilerInstance & CI)898 bool ExtractAPIAction::PrepareToExecuteAction(CompilerInstance &CI) {
899 auto &Inputs = CI.getFrontendOpts().Inputs;
900 if (Inputs.empty())
901 return true;
902
903 if (!CI.hasFileManager())
904 if (!CI.createFileManager())
905 return false;
906
907 auto Kind = Inputs[0].getKind();
908
909 // Convert the header file inputs into a single input buffer.
910 SmallString<256> HeaderContents;
911 bool IsQuoted = false;
912 for (const FrontendInputFile &FIF : Inputs) {
913 if (Kind.isObjectiveC())
914 HeaderContents += "#import";
915 else
916 HeaderContents += "#include";
917
918 StringRef FilePath = FIF.getFile();
919 if (auto RelativeName = getRelativeIncludeName(CI, FilePath, &IsQuoted)) {
920 if (IsQuoted)
921 HeaderContents += " \"";
922 else
923 HeaderContents += " <";
924
925 HeaderContents += *RelativeName;
926
927 if (IsQuoted)
928 HeaderContents += "\"\n";
929 else
930 HeaderContents += ">\n";
931 KnownInputFiles.emplace_back(static_cast<SmallString<32>>(*RelativeName),
932 IsQuoted);
933 } else {
934 HeaderContents += " \"";
935 HeaderContents += FilePath;
936 HeaderContents += "\"\n";
937 KnownInputFiles.emplace_back(FilePath, true);
938 }
939 }
940
941 if (CI.getHeaderSearchOpts().Verbose)
942 CI.getVerboseOutputStream() << getInputBufferName() << ":\n"
943 << HeaderContents << "\n";
944
945 Buffer = llvm::MemoryBuffer::getMemBufferCopy(HeaderContents,
946 getInputBufferName());
947
948 // Set that buffer up as our "real" input in the CompilerInstance.
949 Inputs.clear();
950 Inputs.emplace_back(Buffer->getMemBufferRef(), Kind, /*IsSystem*/ false);
951
952 return true;
953 }
954
EndSourceFileAction()955 void ExtractAPIAction::EndSourceFileAction() {
956 if (!OS)
957 return;
958
959 // Setup a SymbolGraphSerializer to write out collected API information in
960 // the Symbol Graph format.
961 // FIXME: Make the kind of APISerializer configurable.
962 SymbolGraphSerializer SGSerializer(*API, ProductName);
963 SGSerializer.serialize(*OS);
964 OS.reset();
965 }
966
967 std::unique_ptr<raw_pwrite_stream>
CreateOutputFile(CompilerInstance & CI,StringRef InFile)968 ExtractAPIAction::CreateOutputFile(CompilerInstance &CI, StringRef InFile) {
969 std::unique_ptr<raw_pwrite_stream> OS =
970 CI.createDefaultOutputFile(/*Binary=*/false, InFile, /*Extension=*/"json",
971 /*RemoveFileOnSignal=*/false);
972 if (!OS)
973 return nullptr;
974 return OS;
975 }
976