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 54 StringRef getTypedefName(const TagDecl *Decl) { 55 if (const auto *TypedefDecl = Decl->getTypedefNameForAnonDecl()) 56 return TypedefDecl->getName(); 57 58 return {}; 59 } 60 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 { 176 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 217 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: 236 ExtractAPIVisitor(ASTContext &Context, LocationFileChecker &LCF, APISet &API) 237 : Context(Context), API(API), LCF(LCF) {} 238 239 const APISet &getAPI() const { return API; } 240 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 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 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 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 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 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 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 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. 581 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. 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. 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. 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 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 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 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: 782 ExtractAPIConsumer(ASTContext &Context, 783 std::unique_ptr<LocationFileChecker> LCF, APISet &API) 784 : Visitor(Context, *LCF, API), LCF(std::move(LCF)) {} 785 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: 798 MacroCallback(const SourceManager &SM, LocationFileChecker &LCF, APISet &API, 799 Preprocessor &PP) 800 : SM(SM), LCF(LCF), API(API), PP(PP) {} 801 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. 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 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 862 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> 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 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 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> 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