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