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 break; 307 case FunctionDecl::TK_MemberSpecialization: 308 case FunctionDecl::TK_FunctionTemplateSpecialization: 309 if (auto *TemplateInfo = Decl->getTemplateSpecializationInfo()) { 310 if (!TemplateInfo->isExplicitInstantiationOrSpecialization()) 311 return true; 312 } 313 break; 314 case FunctionDecl::TK_FunctionTemplate: 315 case FunctionDecl::TK_DependentFunctionTemplateSpecialization: 316 return true; 317 } 318 319 if (!LCF.isLocationInKnownFile(Decl->getLocation())) 320 return true; 321 322 // Collect symbol information. 323 StringRef Name = Decl->getName(); 324 StringRef USR = API.recordUSR(Decl); 325 PresumedLoc Loc = 326 Context.getSourceManager().getPresumedLoc(Decl->getLocation()); 327 AvailabilityInfo Availability = getAvailability(Decl); 328 LinkageInfo Linkage = Decl->getLinkageAndVisibility(); 329 DocComment Comment; 330 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) 331 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 332 Context.getDiagnostics()); 333 334 // Build declaration fragments, sub-heading, and signature of the function. 335 DeclarationFragments Declaration = 336 DeclarationFragmentsBuilder::getFragmentsForFunction(Decl); 337 DeclarationFragments SubHeading = 338 DeclarationFragmentsBuilder::getSubHeading(Decl); 339 FunctionSignature Signature = 340 DeclarationFragmentsBuilder::getFunctionSignature(Decl); 341 342 // Add the function record to the API set. 343 API.addGlobalFunction(Name, USR, Loc, Availability, Linkage, Comment, 344 Declaration, SubHeading, Signature); 345 return true; 346 } 347 348 bool VisitEnumDecl(const EnumDecl *Decl) { 349 if (!Decl->isComplete()) 350 return true; 351 352 // Skip forward declaration. 353 if (!Decl->isThisDeclarationADefinition()) 354 return true; 355 356 if (!LCF.isLocationInKnownFile(Decl->getLocation())) 357 return true; 358 359 // Collect symbol information. 360 std::string NameString = Decl->getQualifiedNameAsString(); 361 StringRef Name(NameString); 362 if (Name.empty()) 363 Name = getTypedefName(Decl); 364 365 StringRef USR = API.recordUSR(Decl); 366 PresumedLoc Loc = 367 Context.getSourceManager().getPresumedLoc(Decl->getLocation()); 368 AvailabilityInfo Availability = getAvailability(Decl); 369 DocComment Comment; 370 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) 371 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 372 Context.getDiagnostics()); 373 374 // Build declaration fragments and sub-heading for the enum. 375 DeclarationFragments Declaration = 376 DeclarationFragmentsBuilder::getFragmentsForEnum(Decl); 377 DeclarationFragments SubHeading = 378 DeclarationFragmentsBuilder::getSubHeading(Decl); 379 380 EnumRecord *EnumRecord = 381 API.addEnum(API.copyString(Name), USR, Loc, Availability, Comment, 382 Declaration, SubHeading); 383 384 // Now collect information about the enumerators in this enum. 385 recordEnumConstants(EnumRecord, Decl->enumerators()); 386 387 return true; 388 } 389 390 bool VisitRecordDecl(const RecordDecl *Decl) { 391 if (!Decl->isCompleteDefinition()) 392 return true; 393 394 // Skip C++ structs/classes/unions 395 // TODO: support C++ records 396 if (isa<CXXRecordDecl>(Decl)) 397 return true; 398 399 if (!LCF.isLocationInKnownFile(Decl->getLocation())) 400 return true; 401 402 // Collect symbol information. 403 StringRef Name = Decl->getName(); 404 if (Name.empty()) 405 Name = getTypedefName(Decl); 406 StringRef USR = API.recordUSR(Decl); 407 PresumedLoc Loc = 408 Context.getSourceManager().getPresumedLoc(Decl->getLocation()); 409 AvailabilityInfo Availability = getAvailability(Decl); 410 DocComment Comment; 411 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) 412 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 413 Context.getDiagnostics()); 414 415 // Build declaration fragments and sub-heading for the struct. 416 DeclarationFragments Declaration = 417 DeclarationFragmentsBuilder::getFragmentsForStruct(Decl); 418 DeclarationFragments SubHeading = 419 DeclarationFragmentsBuilder::getSubHeading(Decl); 420 421 StructRecord *StructRecord = API.addStruct( 422 Name, USR, Loc, Availability, Comment, Declaration, SubHeading); 423 424 // Now collect information about the fields in this struct. 425 recordStructFields(StructRecord, Decl->fields()); 426 427 return true; 428 } 429 430 bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *Decl) { 431 // Skip forward declaration for classes (@class) 432 if (!Decl->isThisDeclarationADefinition()) 433 return true; 434 435 if (!LCF.isLocationInKnownFile(Decl->getLocation())) 436 return true; 437 438 // Collect symbol information. 439 StringRef Name = Decl->getName(); 440 StringRef USR = API.recordUSR(Decl); 441 PresumedLoc Loc = 442 Context.getSourceManager().getPresumedLoc(Decl->getLocation()); 443 AvailabilityInfo Availability = getAvailability(Decl); 444 LinkageInfo Linkage = Decl->getLinkageAndVisibility(); 445 DocComment Comment; 446 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) 447 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 448 Context.getDiagnostics()); 449 450 // Build declaration fragments and sub-heading for the interface. 451 DeclarationFragments Declaration = 452 DeclarationFragmentsBuilder::getFragmentsForObjCInterface(Decl); 453 DeclarationFragments SubHeading = 454 DeclarationFragmentsBuilder::getSubHeading(Decl); 455 456 // Collect super class information. 457 SymbolReference SuperClass; 458 if (const auto *SuperClassDecl = Decl->getSuperClass()) { 459 SuperClass.Name = SuperClassDecl->getObjCRuntimeNameAsString(); 460 SuperClass.USR = API.recordUSR(SuperClassDecl); 461 } 462 463 ObjCInterfaceRecord *ObjCInterfaceRecord = 464 API.addObjCInterface(Name, USR, Loc, Availability, Linkage, Comment, 465 Declaration, SubHeading, SuperClass); 466 467 // Record all methods (selectors). This doesn't include automatically 468 // synthesized property methods. 469 recordObjCMethods(ObjCInterfaceRecord, Decl->methods()); 470 recordObjCProperties(ObjCInterfaceRecord, Decl->properties()); 471 recordObjCInstanceVariables(ObjCInterfaceRecord, Decl->ivars()); 472 recordObjCProtocols(ObjCInterfaceRecord, Decl->protocols()); 473 474 return true; 475 } 476 477 bool VisitObjCProtocolDecl(const ObjCProtocolDecl *Decl) { 478 // Skip forward declaration for protocols (@protocol). 479 if (!Decl->isThisDeclarationADefinition()) 480 return true; 481 482 if (!LCF.isLocationInKnownFile(Decl->getLocation())) 483 return true; 484 485 // Collect symbol information. 486 StringRef Name = Decl->getName(); 487 StringRef USR = API.recordUSR(Decl); 488 PresumedLoc Loc = 489 Context.getSourceManager().getPresumedLoc(Decl->getLocation()); 490 AvailabilityInfo Availability = getAvailability(Decl); 491 DocComment Comment; 492 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) 493 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 494 Context.getDiagnostics()); 495 496 // Build declaration fragments and sub-heading for the protocol. 497 DeclarationFragments Declaration = 498 DeclarationFragmentsBuilder::getFragmentsForObjCProtocol(Decl); 499 DeclarationFragments SubHeading = 500 DeclarationFragmentsBuilder::getSubHeading(Decl); 501 502 ObjCProtocolRecord *ObjCProtocolRecord = API.addObjCProtocol( 503 Name, USR, Loc, Availability, Comment, Declaration, SubHeading); 504 505 recordObjCMethods(ObjCProtocolRecord, Decl->methods()); 506 recordObjCProperties(ObjCProtocolRecord, Decl->properties()); 507 recordObjCProtocols(ObjCProtocolRecord, Decl->protocols()); 508 509 return true; 510 } 511 512 bool VisitTypedefNameDecl(const TypedefNameDecl *Decl) { 513 // Skip ObjC Type Parameter for now. 514 if (isa<ObjCTypeParamDecl>(Decl)) 515 return true; 516 517 if (!Decl->isDefinedOutsideFunctionOrMethod()) 518 return true; 519 520 if (!LCF.isLocationInKnownFile(Decl->getLocation())) 521 return true; 522 523 PresumedLoc Loc = 524 Context.getSourceManager().getPresumedLoc(Decl->getLocation()); 525 StringRef Name = Decl->getName(); 526 AvailabilityInfo Availability = getAvailability(Decl); 527 StringRef USR = API.recordUSR(Decl); 528 DocComment Comment; 529 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) 530 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 531 Context.getDiagnostics()); 532 533 QualType Type = Decl->getUnderlyingType(); 534 SymbolReference SymRef = 535 TypedefUnderlyingTypeResolver(Context).getSymbolReferenceForType(Type, 536 API); 537 538 API.addTypedef(Name, USR, Loc, Availability, Comment, 539 DeclarationFragmentsBuilder::getFragmentsForTypedef(Decl), 540 DeclarationFragmentsBuilder::getSubHeading(Decl), SymRef); 541 542 return true; 543 } 544 545 bool VisitObjCCategoryDecl(const ObjCCategoryDecl *Decl) { 546 // Collect symbol information. 547 StringRef Name = Decl->getName(); 548 StringRef USR = API.recordUSR(Decl); 549 PresumedLoc Loc = 550 Context.getSourceManager().getPresumedLoc(Decl->getLocation()); 551 AvailabilityInfo Availability = getAvailability(Decl); 552 DocComment Comment; 553 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) 554 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 555 Context.getDiagnostics()); 556 // Build declaration fragments and sub-heading for the category. 557 DeclarationFragments Declaration = 558 DeclarationFragmentsBuilder::getFragmentsForObjCCategory(Decl); 559 DeclarationFragments SubHeading = 560 DeclarationFragmentsBuilder::getSubHeading(Decl); 561 562 const ObjCInterfaceDecl *InterfaceDecl = Decl->getClassInterface(); 563 SymbolReference Interface(InterfaceDecl->getName(), 564 API.recordUSR(InterfaceDecl)); 565 566 ObjCCategoryRecord *ObjCCategoryRecord = 567 API.addObjCCategory(Name, USR, Loc, Availability, Comment, Declaration, 568 SubHeading, Interface); 569 570 recordObjCMethods(ObjCCategoryRecord, Decl->methods()); 571 recordObjCProperties(ObjCCategoryRecord, Decl->properties()); 572 recordObjCInstanceVariables(ObjCCategoryRecord, Decl->ivars()); 573 recordObjCProtocols(ObjCCategoryRecord, Decl->protocols()); 574 575 return true; 576 } 577 578 private: 579 /// Get availability information of the declaration \p D. 580 AvailabilityInfo getAvailability(const Decl *D) const { 581 StringRef PlatformName = Context.getTargetInfo().getPlatformName(); 582 583 AvailabilityInfo Availability; 584 // Collect availability attributes from all redeclarations. 585 for (const auto *RD : D->redecls()) { 586 for (const auto *A : RD->specific_attrs<AvailabilityAttr>()) { 587 if (A->getPlatform()->getName() != PlatformName) 588 continue; 589 Availability = AvailabilityInfo(A->getIntroduced(), A->getDeprecated(), 590 A->getObsoleted(), A->getUnavailable(), 591 /* UnconditionallyDeprecated */ false, 592 /* UnconditionallyUnavailable */ false); 593 break; 594 } 595 596 if (const auto *A = RD->getAttr<UnavailableAttr>()) 597 if (!A->isImplicit()) { 598 Availability.Unavailable = true; 599 Availability.UnconditionallyUnavailable = true; 600 } 601 602 if (const auto *A = RD->getAttr<DeprecatedAttr>()) 603 if (!A->isImplicit()) 604 Availability.UnconditionallyDeprecated = true; 605 } 606 607 return Availability; 608 } 609 610 /// Collect API information for the enum constants and associate with the 611 /// parent enum. 612 void recordEnumConstants(EnumRecord *EnumRecord, 613 const EnumDecl::enumerator_range Constants) { 614 for (const auto *Constant : Constants) { 615 // Collect symbol information. 616 StringRef Name = Constant->getName(); 617 StringRef USR = API.recordUSR(Constant); 618 PresumedLoc Loc = 619 Context.getSourceManager().getPresumedLoc(Constant->getLocation()); 620 AvailabilityInfo Availability = getAvailability(Constant); 621 DocComment Comment; 622 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Constant)) 623 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 624 Context.getDiagnostics()); 625 626 // Build declaration fragments and sub-heading for the enum constant. 627 DeclarationFragments Declaration = 628 DeclarationFragmentsBuilder::getFragmentsForEnumConstant(Constant); 629 DeclarationFragments SubHeading = 630 DeclarationFragmentsBuilder::getSubHeading(Constant); 631 632 API.addEnumConstant(EnumRecord, Name, USR, Loc, Availability, Comment, 633 Declaration, SubHeading); 634 } 635 } 636 637 /// Collect API information for the struct fields and associate with the 638 /// parent struct. 639 void recordStructFields(StructRecord *StructRecord, 640 const RecordDecl::field_range Fields) { 641 for (const auto *Field : Fields) { 642 // Collect symbol information. 643 StringRef Name = Field->getName(); 644 StringRef USR = API.recordUSR(Field); 645 PresumedLoc Loc = 646 Context.getSourceManager().getPresumedLoc(Field->getLocation()); 647 AvailabilityInfo Availability = getAvailability(Field); 648 DocComment Comment; 649 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Field)) 650 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 651 Context.getDiagnostics()); 652 653 // Build declaration fragments and sub-heading for the struct field. 654 DeclarationFragments Declaration = 655 DeclarationFragmentsBuilder::getFragmentsForField(Field); 656 DeclarationFragments SubHeading = 657 DeclarationFragmentsBuilder::getSubHeading(Field); 658 659 API.addStructField(StructRecord, Name, USR, Loc, Availability, Comment, 660 Declaration, SubHeading); 661 } 662 } 663 664 /// Collect API information for the Objective-C methods and associate with the 665 /// parent container. 666 void recordObjCMethods(ObjCContainerRecord *Container, 667 const ObjCContainerDecl::method_range Methods) { 668 for (const auto *Method : Methods) { 669 // Don't record selectors for properties. 670 if (Method->isPropertyAccessor()) 671 continue; 672 673 StringRef Name = API.copyString(Method->getSelector().getAsString()); 674 StringRef USR = API.recordUSR(Method); 675 PresumedLoc Loc = 676 Context.getSourceManager().getPresumedLoc(Method->getLocation()); 677 AvailabilityInfo Availability = getAvailability(Method); 678 DocComment Comment; 679 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Method)) 680 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 681 Context.getDiagnostics()); 682 683 // Build declaration fragments, sub-heading, and signature for the method. 684 DeclarationFragments Declaration = 685 DeclarationFragmentsBuilder::getFragmentsForObjCMethod(Method); 686 DeclarationFragments SubHeading = 687 DeclarationFragmentsBuilder::getSubHeading(Method); 688 FunctionSignature Signature = 689 DeclarationFragmentsBuilder::getFunctionSignature(Method); 690 691 API.addObjCMethod(Container, Name, USR, Loc, Availability, Comment, 692 Declaration, SubHeading, Signature, 693 Method->isInstanceMethod()); 694 } 695 } 696 697 void recordObjCProperties(ObjCContainerRecord *Container, 698 const ObjCContainerDecl::prop_range Properties) { 699 for (const auto *Property : Properties) { 700 StringRef Name = Property->getName(); 701 StringRef USR = API.recordUSR(Property); 702 PresumedLoc Loc = 703 Context.getSourceManager().getPresumedLoc(Property->getLocation()); 704 AvailabilityInfo Availability = getAvailability(Property); 705 DocComment Comment; 706 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Property)) 707 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 708 Context.getDiagnostics()); 709 710 // Build declaration fragments and sub-heading for the property. 711 DeclarationFragments Declaration = 712 DeclarationFragmentsBuilder::getFragmentsForObjCProperty(Property); 713 DeclarationFragments SubHeading = 714 DeclarationFragmentsBuilder::getSubHeading(Property); 715 716 StringRef GetterName = 717 API.copyString(Property->getGetterName().getAsString()); 718 StringRef SetterName = 719 API.copyString(Property->getSetterName().getAsString()); 720 721 // Get the attributes for property. 722 unsigned Attributes = ObjCPropertyRecord::NoAttr; 723 if (Property->getPropertyAttributes() & 724 ObjCPropertyAttribute::kind_readonly) 725 Attributes |= ObjCPropertyRecord::ReadOnly; 726 if (Property->getPropertyAttributes() & ObjCPropertyAttribute::kind_class) 727 Attributes |= ObjCPropertyRecord::Class; 728 729 API.addObjCProperty( 730 Container, Name, USR, Loc, Availability, Comment, Declaration, 731 SubHeading, 732 static_cast<ObjCPropertyRecord::AttributeKind>(Attributes), 733 GetterName, SetterName, Property->isOptional()); 734 } 735 } 736 737 void recordObjCInstanceVariables( 738 ObjCContainerRecord *Container, 739 const llvm::iterator_range< 740 DeclContext::specific_decl_iterator<ObjCIvarDecl>> 741 Ivars) { 742 for (const auto *Ivar : Ivars) { 743 StringRef Name = Ivar->getName(); 744 StringRef USR = API.recordUSR(Ivar); 745 PresumedLoc Loc = 746 Context.getSourceManager().getPresumedLoc(Ivar->getLocation()); 747 AvailabilityInfo Availability = getAvailability(Ivar); 748 DocComment Comment; 749 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Ivar)) 750 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 751 Context.getDiagnostics()); 752 753 // Build declaration fragments and sub-heading for the instance variable. 754 DeclarationFragments Declaration = 755 DeclarationFragmentsBuilder::getFragmentsForField(Ivar); 756 DeclarationFragments SubHeading = 757 DeclarationFragmentsBuilder::getSubHeading(Ivar); 758 759 ObjCInstanceVariableRecord::AccessControl Access = 760 Ivar->getCanonicalAccessControl(); 761 762 API.addObjCInstanceVariable(Container, Name, USR, Loc, Availability, 763 Comment, Declaration, SubHeading, Access); 764 } 765 } 766 767 void recordObjCProtocols(ObjCContainerRecord *Container, 768 ObjCInterfaceDecl::protocol_range Protocols) { 769 for (const auto *Protocol : Protocols) 770 Container->Protocols.emplace_back(Protocol->getName(), 771 API.recordUSR(Protocol)); 772 } 773 774 ASTContext &Context; 775 APISet &API; 776 LocationFileChecker &LCF; 777 }; 778 779 class ExtractAPIConsumer : public ASTConsumer { 780 public: 781 ExtractAPIConsumer(ASTContext &Context, 782 std::unique_ptr<LocationFileChecker> LCF, APISet &API) 783 : Visitor(Context, *LCF, API), LCF(std::move(LCF)) {} 784 785 void HandleTranslationUnit(ASTContext &Context) override { 786 // Use ExtractAPIVisitor to traverse symbol declarations in the context. 787 Visitor.TraverseDecl(Context.getTranslationUnitDecl()); 788 } 789 790 private: 791 ExtractAPIVisitor Visitor; 792 std::unique_ptr<LocationFileChecker> LCF; 793 }; 794 795 class MacroCallback : public PPCallbacks { 796 public: 797 MacroCallback(const SourceManager &SM, LocationFileChecker &LCF, APISet &API, 798 Preprocessor &PP) 799 : SM(SM), LCF(LCF), API(API), PP(PP) {} 800 801 void MacroDefined(const Token &MacroNameToken, 802 const MacroDirective *MD) override { 803 auto *MacroInfo = MD->getMacroInfo(); 804 805 if (MacroInfo->isBuiltinMacro()) 806 return; 807 808 auto SourceLoc = MacroNameToken.getLocation(); 809 if (SM.isWrittenInBuiltinFile(SourceLoc) || 810 SM.isWrittenInCommandLineFile(SourceLoc)) 811 return; 812 813 PendingMacros.emplace_back(MacroNameToken, MD); 814 } 815 816 // If a macro gets undefined at some point during preprocessing of the inputs 817 // it means that it isn't an exposed API and we should therefore not add a 818 // macro definition for it. 819 void MacroUndefined(const Token &MacroNameToken, const MacroDefinition &MD, 820 const MacroDirective *Undef) override { 821 // If this macro wasn't previously defined we don't need to do anything 822 // here. 823 if (!Undef) 824 return; 825 826 llvm::erase_if(PendingMacros, [&MD, this](const PendingMacro &PM) { 827 return MD.getMacroInfo()->isIdenticalTo(*PM.MD->getMacroInfo(), PP, 828 /*Syntactically*/ false); 829 }); 830 } 831 832 void EndOfMainFile() override { 833 for (auto &PM : PendingMacros) { 834 // `isUsedForHeaderGuard` is only set when the preprocessor leaves the 835 // file so check for it here. 836 if (PM.MD->getMacroInfo()->isUsedForHeaderGuard()) 837 continue; 838 839 if (!LCF.isLocationInKnownFile(PM.MacroNameToken.getLocation())) 840 continue; 841 842 StringRef Name = PM.MacroNameToken.getIdentifierInfo()->getName(); 843 PresumedLoc Loc = SM.getPresumedLoc(PM.MacroNameToken.getLocation()); 844 StringRef USR = 845 API.recordUSRForMacro(Name, PM.MacroNameToken.getLocation(), SM); 846 847 API.addMacroDefinition( 848 Name, USR, Loc, 849 DeclarationFragmentsBuilder::getFragmentsForMacro(Name, PM.MD), 850 DeclarationFragmentsBuilder::getSubHeadingForMacro(Name)); 851 } 852 853 PendingMacros.clear(); 854 } 855 856 private: 857 struct PendingMacro { 858 Token MacroNameToken; 859 const MacroDirective *MD; 860 861 PendingMacro(const Token &MacroNameToken, const MacroDirective *MD) 862 : MacroNameToken(MacroNameToken), MD(MD) {} 863 }; 864 865 const SourceManager &SM; 866 LocationFileChecker &LCF; 867 APISet &API; 868 Preprocessor &PP; 869 llvm::SmallVector<PendingMacro> PendingMacros; 870 }; 871 872 } // namespace 873 874 std::unique_ptr<ASTConsumer> 875 ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { 876 OS = CreateOutputFile(CI, InFile); 877 if (!OS) 878 return nullptr; 879 880 ProductName = CI.getFrontendOpts().ProductName; 881 882 // Now that we have enough information about the language options and the 883 // target triple, let's create the APISet before anyone uses it. 884 API = std::make_unique<APISet>( 885 CI.getTarget().getTriple(), 886 CI.getFrontendOpts().Inputs.back().getKind().getLanguage()); 887 888 auto LCF = std::make_unique<LocationFileChecker>(CI, KnownInputFiles); 889 890 CI.getPreprocessor().addPPCallbacks(std::make_unique<MacroCallback>( 891 CI.getSourceManager(), *LCF, *API, CI.getPreprocessor())); 892 893 return std::make_unique<ExtractAPIConsumer>(CI.getASTContext(), 894 std::move(LCF), *API); 895 } 896 897 bool ExtractAPIAction::PrepareToExecuteAction(CompilerInstance &CI) { 898 auto &Inputs = CI.getFrontendOpts().Inputs; 899 if (Inputs.empty()) 900 return true; 901 902 if (!CI.hasFileManager()) 903 if (!CI.createFileManager()) 904 return false; 905 906 auto Kind = Inputs[0].getKind(); 907 908 // Convert the header file inputs into a single input buffer. 909 SmallString<256> HeaderContents; 910 bool IsQuoted = false; 911 for (const FrontendInputFile &FIF : Inputs) { 912 if (Kind.isObjectiveC()) 913 HeaderContents += "#import"; 914 else 915 HeaderContents += "#include"; 916 917 StringRef FilePath = FIF.getFile(); 918 if (auto RelativeName = getRelativeIncludeName(CI, FilePath, &IsQuoted)) { 919 if (IsQuoted) 920 HeaderContents += " \""; 921 else 922 HeaderContents += " <"; 923 924 HeaderContents += *RelativeName; 925 926 if (IsQuoted) 927 HeaderContents += "\"\n"; 928 else 929 HeaderContents += ">\n"; 930 KnownInputFiles.emplace_back(static_cast<SmallString<32>>(*RelativeName), 931 IsQuoted); 932 } else { 933 HeaderContents += " \""; 934 HeaderContents += FilePath; 935 HeaderContents += "\"\n"; 936 KnownInputFiles.emplace_back(FilePath, true); 937 } 938 } 939 940 if (CI.getHeaderSearchOpts().Verbose) 941 CI.getVerboseOutputStream() << getInputBufferName() << ":\n" 942 << HeaderContents << "\n"; 943 944 Buffer = llvm::MemoryBuffer::getMemBufferCopy(HeaderContents, 945 getInputBufferName()); 946 947 // Set that buffer up as our "real" input in the CompilerInstance. 948 Inputs.clear(); 949 Inputs.emplace_back(Buffer->getMemBufferRef(), Kind, /*IsSystem*/ false); 950 951 return true; 952 } 953 954 void ExtractAPIAction::EndSourceFileAction() { 955 if (!OS) 956 return; 957 958 // Setup a SymbolGraphSerializer to write out collected API information in 959 // the Symbol Graph format. 960 // FIXME: Make the kind of APISerializer configurable. 961 SymbolGraphSerializer SGSerializer(*API, ProductName); 962 SGSerializer.serialize(*OS); 963 OS.reset(); 964 } 965 966 std::unique_ptr<raw_pwrite_stream> 967 ExtractAPIAction::CreateOutputFile(CompilerInstance &CI, StringRef InFile) { 968 std::unique_ptr<raw_pwrite_stream> OS = 969 CI.createDefaultOutputFile(/*Binary=*/false, InFile, /*Extension=*/"json", 970 /*RemoveFileOnSignal=*/false); 971 if (!OS) 972 return nullptr; 973 return OS; 974 } 975