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/DiagnosticFrontend.h" 24 #include "clang/Basic/SourceLocation.h" 25 #include "clang/Basic/SourceManager.h" 26 #include "clang/Basic/TargetInfo.h" 27 #include "clang/ExtractAPI/API.h" 28 #include "clang/ExtractAPI/APIIgnoresList.h" 29 #include "clang/ExtractAPI/AvailabilityInfo.h" 30 #include "clang/ExtractAPI/DeclarationFragments.h" 31 #include "clang/ExtractAPI/FrontendActions.h" 32 #include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h" 33 #include "clang/Frontend/ASTConsumers.h" 34 #include "clang/Frontend/CompilerInstance.h" 35 #include "clang/Frontend/FrontendOptions.h" 36 #include "clang/Lex/MacroInfo.h" 37 #include "clang/Lex/PPCallbacks.h" 38 #include "clang/Lex/Preprocessor.h" 39 #include "clang/Lex/PreprocessorOptions.h" 40 #include "llvm/ADT/DenseSet.h" 41 #include "llvm/ADT/STLExtras.h" 42 #include "llvm/ADT/SmallVector.h" 43 #include "llvm/Support/Error.h" 44 #include "llvm/Support/FileSystem.h" 45 #include "llvm/Support/MemoryBuffer.h" 46 #include "llvm/Support/Path.h" 47 #include "llvm/Support/Regex.h" 48 #include "llvm/Support/raw_ostream.h" 49 #include <memory> 50 #include <utility> 51 52 using namespace clang; 53 using namespace extractapi; 54 55 namespace { 56 57 StringRef getTypedefName(const TagDecl *Decl) { 58 if (const auto *TypedefDecl = Decl->getTypedefNameForAnonDecl()) 59 return TypedefDecl->getName(); 60 61 return {}; 62 } 63 64 Optional<std::string> getRelativeIncludeName(const CompilerInstance &CI, 65 StringRef File, 66 bool *IsQuoted = nullptr) { 67 assert(CI.hasFileManager() && 68 "CompilerInstance does not have a FileNamager!"); 69 70 using namespace llvm::sys; 71 // Matches framework include patterns 72 const llvm::Regex Rule("/(.+)\\.framework/(.+)?Headers/(.+)"); 73 74 const auto &FS = CI.getVirtualFileSystem(); 75 76 SmallString<128> FilePath(File.begin(), File.end()); 77 FS.makeAbsolute(FilePath); 78 path::remove_dots(FilePath, true); 79 FilePath = path::convert_to_slash(FilePath); 80 File = FilePath; 81 82 // Checks whether `Dir` is a strict path prefix of `File`. If so returns 83 // the prefix length. Otherwise return 0. 84 auto CheckDir = [&](llvm::StringRef Dir) -> unsigned { 85 llvm::SmallString<32> DirPath(Dir.begin(), Dir.end()); 86 FS.makeAbsolute(DirPath); 87 path::remove_dots(DirPath, true); 88 Dir = DirPath; 89 for (auto NI = path::begin(File), NE = path::end(File), 90 DI = path::begin(Dir), DE = path::end(Dir); 91 /*termination condition in loop*/; ++NI, ++DI) { 92 // '.' components in File are ignored. 93 while (NI != NE && *NI == ".") 94 ++NI; 95 if (NI == NE) 96 break; 97 98 // '.' components in Dir are ignored. 99 while (DI != DE && *DI == ".") 100 ++DI; 101 102 // Dir is a prefix of File, up to '.' components and choice of path 103 // separators. 104 if (DI == DE) 105 return NI - path::begin(File); 106 107 // Consider all path separators equal. 108 if (NI->size() == 1 && DI->size() == 1 && 109 path::is_separator(NI->front()) && path::is_separator(DI->front())) 110 continue; 111 112 // Special case Apple .sdk folders since the search path is typically a 113 // symlink like `iPhoneSimulator14.5.sdk` while the file is instead 114 // located in `iPhoneSimulator.sdk` (the real folder). 115 if (NI->endswith(".sdk") && DI->endswith(".sdk")) { 116 StringRef NBasename = path::stem(*NI); 117 StringRef DBasename = path::stem(*DI); 118 if (DBasename.startswith(NBasename)) 119 continue; 120 } 121 122 if (*NI != *DI) 123 break; 124 } 125 return 0; 126 }; 127 128 unsigned PrefixLength = 0; 129 130 // Go through the search paths and find the first one that is a prefix of 131 // the header. 132 for (const auto &Entry : CI.getHeaderSearchOpts().UserEntries) { 133 // Note whether the match is found in a quoted entry. 134 if (IsQuoted) 135 *IsQuoted = Entry.Group == frontend::Quoted; 136 137 if (auto EntryFile = CI.getFileManager().getOptionalFileRef(Entry.Path)) { 138 if (auto HMap = HeaderMap::Create(*EntryFile, CI.getFileManager())) { 139 // If this is a headermap entry, try to reverse lookup the full path 140 // for a spelled name before mapping. 141 StringRef SpelledFilename = HMap->reverseLookupFilename(File); 142 if (!SpelledFilename.empty()) 143 return SpelledFilename.str(); 144 145 // No matching mapping in this headermap, try next search entry. 146 continue; 147 } 148 } 149 150 // Entry is a directory search entry, try to check if it's a prefix of File. 151 PrefixLength = CheckDir(Entry.Path); 152 if (PrefixLength > 0) { 153 // The header is found in a framework path, construct the framework-style 154 // include name `<Framework/Header.h>` 155 if (Entry.IsFramework) { 156 SmallVector<StringRef, 4> Matches; 157 Rule.match(File, &Matches); 158 // Returned matches are always in stable order. 159 if (Matches.size() != 4) 160 return std::nullopt; 161 162 return path::convert_to_slash( 163 (Matches[1].drop_front(Matches[1].rfind('/') + 1) + "/" + 164 Matches[3]) 165 .str()); 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 std::nullopt; 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::any_of(KnownFiles, 206 [&IsQuoted, &IncludeName](const auto &KnownFile) { 207 return KnownFile.first.equals(*IncludeName) && 208 KnownFile.second == IsQuoted; 209 })) { 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 LinkageInfo Linkage = Decl->getLinkageAndVisibility(); 271 DocComment Comment; 272 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) 273 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 274 Context.getDiagnostics()); 275 276 // Build declaration fragments and sub-heading for the variable. 277 DeclarationFragments Declaration = 278 DeclarationFragmentsBuilder::getFragmentsForVar(Decl); 279 DeclarationFragments SubHeading = 280 DeclarationFragmentsBuilder::getSubHeading(Decl); 281 282 // Add the global variable record to the API set. 283 API.addGlobalVar(Name, USR, Loc, AvailabilitySet(Decl), Linkage, Comment, 284 Declaration, SubHeading); 285 return true; 286 } 287 288 bool VisitFunctionDecl(const FunctionDecl *Decl) { 289 if (const auto *Method = dyn_cast<CXXMethodDecl>(Decl)) { 290 // Skip member function in class templates. 291 if (Method->getParent()->getDescribedClassTemplate() != nullptr) 292 return true; 293 294 // Skip methods in records. 295 for (auto P : Context.getParents(*Method)) { 296 if (P.get<CXXRecordDecl>()) 297 return true; 298 } 299 300 // Skip ConstructorDecl and DestructorDecl. 301 if (isa<CXXConstructorDecl>(Method) || isa<CXXDestructorDecl>(Method)) 302 return true; 303 } 304 305 // Skip templated functions. 306 switch (Decl->getTemplatedKind()) { 307 case FunctionDecl::TK_NonTemplate: 308 case FunctionDecl::TK_DependentNonTemplate: 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 LinkageInfo Linkage = Decl->getLinkageAndVisibility(); 331 DocComment Comment; 332 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) 333 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 334 Context.getDiagnostics()); 335 336 // Build declaration fragments, sub-heading, and signature of the function. 337 DeclarationFragments Declaration = 338 DeclarationFragmentsBuilder::getFragmentsForFunction(Decl); 339 DeclarationFragments SubHeading = 340 DeclarationFragmentsBuilder::getSubHeading(Decl); 341 FunctionSignature Signature = 342 DeclarationFragmentsBuilder::getFunctionSignature(Decl); 343 344 // Add the function record to the API set. 345 API.addGlobalFunction(Name, USR, Loc, AvailabilitySet(Decl), Linkage, 346 Comment, Declaration, SubHeading, Signature); 347 return true; 348 } 349 350 bool VisitEnumDecl(const EnumDecl *Decl) { 351 if (!Decl->isComplete()) 352 return true; 353 354 // Skip forward declaration. 355 if (!Decl->isThisDeclarationADefinition()) 356 return true; 357 358 if (!LCF.isLocationInKnownFile(Decl->getLocation())) 359 return true; 360 361 // Collect symbol information. 362 std::string NameString = Decl->getQualifiedNameAsString(); 363 StringRef Name(NameString); 364 if (Name.empty()) 365 Name = getTypedefName(Decl); 366 367 StringRef USR = API.recordUSR(Decl); 368 PresumedLoc Loc = 369 Context.getSourceManager().getPresumedLoc(Decl->getLocation()); 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, AvailabilitySet(Decl), 383 Comment, 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 if (Name.empty()) 408 return true; 409 410 StringRef USR = API.recordUSR(Decl); 411 PresumedLoc Loc = 412 Context.getSourceManager().getPresumedLoc(Decl->getLocation()); 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 = 425 API.addStruct(Name, USR, Loc, AvailabilitySet(Decl), Comment, 426 Declaration, SubHeading); 427 428 // Now collect information about the fields in this struct. 429 recordStructFields(StructRecord, Decl->fields()); 430 431 return true; 432 } 433 434 bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *Decl) { 435 // Skip forward declaration for classes (@class) 436 if (!Decl->isThisDeclarationADefinition()) 437 return true; 438 439 if (!LCF.isLocationInKnownFile(Decl->getLocation())) 440 return true; 441 442 // Collect symbol information. 443 StringRef Name = Decl->getName(); 444 StringRef USR = API.recordUSR(Decl); 445 PresumedLoc Loc = 446 Context.getSourceManager().getPresumedLoc(Decl->getLocation()); 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, AvailabilitySet(Decl), Linkage, 468 Comment, 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 DocComment Comment; 494 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) 495 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 496 Context.getDiagnostics()); 497 498 // Build declaration fragments and sub-heading for the protocol. 499 DeclarationFragments Declaration = 500 DeclarationFragmentsBuilder::getFragmentsForObjCProtocol(Decl); 501 DeclarationFragments SubHeading = 502 DeclarationFragmentsBuilder::getSubHeading(Decl); 503 504 ObjCProtocolRecord *ObjCProtocolRecord = 505 API.addObjCProtocol(Name, USR, Loc, AvailabilitySet(Decl), Comment, 506 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 StringRef USR = API.recordUSR(Decl); 530 DocComment Comment; 531 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) 532 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 533 Context.getDiagnostics()); 534 535 QualType Type = Decl->getUnderlyingType(); 536 SymbolReference SymRef = 537 TypedefUnderlyingTypeResolver(Context).getSymbolReferenceForType(Type, 538 API); 539 540 API.addTypedef(Name, USR, Loc, AvailabilitySet(Decl), Comment, 541 DeclarationFragmentsBuilder::getFragmentsForTypedef(Decl), 542 DeclarationFragmentsBuilder::getSubHeading(Decl), SymRef); 543 544 return true; 545 } 546 547 bool VisitObjCCategoryDecl(const ObjCCategoryDecl *Decl) { 548 // Collect symbol information. 549 StringRef Name = Decl->getName(); 550 StringRef USR = API.recordUSR(Decl); 551 PresumedLoc Loc = 552 Context.getSourceManager().getPresumedLoc(Decl->getLocation()); 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, AvailabilitySet(Decl), Comment, 569 Declaration, 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 /// Collect API information for the enum constants and associate with the 581 /// parent enum. 582 void recordEnumConstants(EnumRecord *EnumRecord, 583 const EnumDecl::enumerator_range Constants) { 584 for (const auto *Constant : Constants) { 585 // Collect symbol information. 586 StringRef Name = Constant->getName(); 587 StringRef USR = API.recordUSR(Constant); 588 PresumedLoc Loc = 589 Context.getSourceManager().getPresumedLoc(Constant->getLocation()); 590 DocComment Comment; 591 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Constant)) 592 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 593 Context.getDiagnostics()); 594 595 // Build declaration fragments and sub-heading for the enum constant. 596 DeclarationFragments Declaration = 597 DeclarationFragmentsBuilder::getFragmentsForEnumConstant(Constant); 598 DeclarationFragments SubHeading = 599 DeclarationFragmentsBuilder::getSubHeading(Constant); 600 601 API.addEnumConstant(EnumRecord, Name, USR, Loc, AvailabilitySet(Constant), 602 Comment, Declaration, SubHeading); 603 } 604 } 605 606 /// Collect API information for the struct fields and associate with the 607 /// parent struct. 608 void recordStructFields(StructRecord *StructRecord, 609 const RecordDecl::field_range Fields) { 610 for (const auto *Field : Fields) { 611 // Collect symbol information. 612 StringRef Name = Field->getName(); 613 StringRef USR = API.recordUSR(Field); 614 PresumedLoc Loc = 615 Context.getSourceManager().getPresumedLoc(Field->getLocation()); 616 DocComment Comment; 617 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Field)) 618 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 619 Context.getDiagnostics()); 620 621 // Build declaration fragments and sub-heading for the struct field. 622 DeclarationFragments Declaration = 623 DeclarationFragmentsBuilder::getFragmentsForField(Field); 624 DeclarationFragments SubHeading = 625 DeclarationFragmentsBuilder::getSubHeading(Field); 626 627 API.addStructField(StructRecord, Name, USR, Loc, AvailabilitySet(Field), 628 Comment, Declaration, SubHeading); 629 } 630 } 631 632 /// Collect API information for the Objective-C methods and associate with the 633 /// parent container. 634 void recordObjCMethods(ObjCContainerRecord *Container, 635 const ObjCContainerDecl::method_range Methods) { 636 for (const auto *Method : Methods) { 637 // Don't record selectors for properties. 638 if (Method->isPropertyAccessor()) 639 continue; 640 641 StringRef Name = API.copyString(Method->getSelector().getAsString()); 642 StringRef USR = API.recordUSR(Method); 643 PresumedLoc Loc = 644 Context.getSourceManager().getPresumedLoc(Method->getLocation()); 645 DocComment Comment; 646 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Method)) 647 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 648 Context.getDiagnostics()); 649 650 // Build declaration fragments, sub-heading, and signature for the method. 651 DeclarationFragments Declaration = 652 DeclarationFragmentsBuilder::getFragmentsForObjCMethod(Method); 653 DeclarationFragments SubHeading = 654 DeclarationFragmentsBuilder::getSubHeading(Method); 655 FunctionSignature Signature = 656 DeclarationFragmentsBuilder::getFunctionSignature(Method); 657 658 API.addObjCMethod(Container, Name, USR, Loc, AvailabilitySet(Method), 659 Comment, Declaration, SubHeading, Signature, 660 Method->isInstanceMethod()); 661 } 662 } 663 664 void recordObjCProperties(ObjCContainerRecord *Container, 665 const ObjCContainerDecl::prop_range Properties) { 666 for (const auto *Property : Properties) { 667 StringRef Name = Property->getName(); 668 StringRef USR = API.recordUSR(Property); 669 PresumedLoc Loc = 670 Context.getSourceManager().getPresumedLoc(Property->getLocation()); 671 DocComment Comment; 672 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Property)) 673 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 674 Context.getDiagnostics()); 675 676 // Build declaration fragments and sub-heading for the property. 677 DeclarationFragments Declaration = 678 DeclarationFragmentsBuilder::getFragmentsForObjCProperty(Property); 679 DeclarationFragments SubHeading = 680 DeclarationFragmentsBuilder::getSubHeading(Property); 681 682 StringRef GetterName = 683 API.copyString(Property->getGetterName().getAsString()); 684 StringRef SetterName = 685 API.copyString(Property->getSetterName().getAsString()); 686 687 // Get the attributes for property. 688 unsigned Attributes = ObjCPropertyRecord::NoAttr; 689 if (Property->getPropertyAttributes() & 690 ObjCPropertyAttribute::kind_readonly) 691 Attributes |= ObjCPropertyRecord::ReadOnly; 692 if (Property->getPropertyAttributes() & ObjCPropertyAttribute::kind_class) 693 Attributes |= ObjCPropertyRecord::Class; 694 695 API.addObjCProperty( 696 Container, Name, USR, Loc, AvailabilitySet(Property), Comment, 697 Declaration, SubHeading, 698 static_cast<ObjCPropertyRecord::AttributeKind>(Attributes), 699 GetterName, SetterName, Property->isOptional()); 700 } 701 } 702 703 void recordObjCInstanceVariables( 704 ObjCContainerRecord *Container, 705 const llvm::iterator_range< 706 DeclContext::specific_decl_iterator<ObjCIvarDecl>> 707 Ivars) { 708 for (const auto *Ivar : Ivars) { 709 StringRef Name = Ivar->getName(); 710 StringRef USR = API.recordUSR(Ivar); 711 PresumedLoc Loc = 712 Context.getSourceManager().getPresumedLoc(Ivar->getLocation()); 713 DocComment Comment; 714 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Ivar)) 715 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 716 Context.getDiagnostics()); 717 718 // Build declaration fragments and sub-heading for the instance variable. 719 DeclarationFragments Declaration = 720 DeclarationFragmentsBuilder::getFragmentsForField(Ivar); 721 DeclarationFragments SubHeading = 722 DeclarationFragmentsBuilder::getSubHeading(Ivar); 723 724 ObjCInstanceVariableRecord::AccessControl Access = 725 Ivar->getCanonicalAccessControl(); 726 727 API.addObjCInstanceVariable(Container, Name, USR, Loc, 728 AvailabilitySet(Ivar), Comment, Declaration, 729 SubHeading, Access); 730 } 731 } 732 733 void recordObjCProtocols(ObjCContainerRecord *Container, 734 ObjCInterfaceDecl::protocol_range Protocols) { 735 for (const auto *Protocol : Protocols) 736 Container->Protocols.emplace_back(Protocol->getName(), 737 API.recordUSR(Protocol)); 738 } 739 740 ASTContext &Context; 741 APISet &API; 742 LocationFileChecker &LCF; 743 }; 744 745 class ExtractAPIConsumer : public ASTConsumer { 746 public: 747 ExtractAPIConsumer(ASTContext &Context, 748 std::unique_ptr<LocationFileChecker> LCF, APISet &API) 749 : Visitor(Context, *LCF, API), LCF(std::move(LCF)) {} 750 751 void HandleTranslationUnit(ASTContext &Context) override { 752 // Use ExtractAPIVisitor to traverse symbol declarations in the context. 753 Visitor.TraverseDecl(Context.getTranslationUnitDecl()); 754 } 755 756 private: 757 ExtractAPIVisitor Visitor; 758 std::unique_ptr<LocationFileChecker> LCF; 759 }; 760 761 class MacroCallback : public PPCallbacks { 762 public: 763 MacroCallback(const SourceManager &SM, LocationFileChecker &LCF, APISet &API, 764 Preprocessor &PP) 765 : SM(SM), LCF(LCF), API(API), PP(PP) {} 766 767 void MacroDefined(const Token &MacroNameToken, 768 const MacroDirective *MD) override { 769 auto *MacroInfo = MD->getMacroInfo(); 770 771 if (MacroInfo->isBuiltinMacro()) 772 return; 773 774 auto SourceLoc = MacroNameToken.getLocation(); 775 if (SM.isWrittenInBuiltinFile(SourceLoc) || 776 SM.isWrittenInCommandLineFile(SourceLoc)) 777 return; 778 779 PendingMacros.emplace_back(MacroNameToken, MD); 780 } 781 782 // If a macro gets undefined at some point during preprocessing of the inputs 783 // it means that it isn't an exposed API and we should therefore not add a 784 // macro definition for it. 785 void MacroUndefined(const Token &MacroNameToken, const MacroDefinition &MD, 786 const MacroDirective *Undef) override { 787 // If this macro wasn't previously defined we don't need to do anything 788 // here. 789 if (!Undef) 790 return; 791 792 llvm::erase_if(PendingMacros, [&MD, this](const PendingMacro &PM) { 793 return MD.getMacroInfo()->isIdenticalTo(*PM.MD->getMacroInfo(), PP, 794 /*Syntactically*/ false); 795 }); 796 } 797 798 void EndOfMainFile() override { 799 for (auto &PM : PendingMacros) { 800 // `isUsedForHeaderGuard` is only set when the preprocessor leaves the 801 // file so check for it here. 802 if (PM.MD->getMacroInfo()->isUsedForHeaderGuard()) 803 continue; 804 805 if (!LCF.isLocationInKnownFile(PM.MacroNameToken.getLocation())) 806 continue; 807 808 StringRef Name = PM.MacroNameToken.getIdentifierInfo()->getName(); 809 PresumedLoc Loc = SM.getPresumedLoc(PM.MacroNameToken.getLocation()); 810 StringRef USR = 811 API.recordUSRForMacro(Name, PM.MacroNameToken.getLocation(), SM); 812 813 API.addMacroDefinition( 814 Name, USR, Loc, 815 DeclarationFragmentsBuilder::getFragmentsForMacro(Name, PM.MD), 816 DeclarationFragmentsBuilder::getSubHeadingForMacro(Name)); 817 } 818 819 PendingMacros.clear(); 820 } 821 822 private: 823 struct PendingMacro { 824 Token MacroNameToken; 825 const MacroDirective *MD; 826 827 PendingMacro(const Token &MacroNameToken, const MacroDirective *MD) 828 : MacroNameToken(MacroNameToken), MD(MD) {} 829 }; 830 831 const SourceManager &SM; 832 LocationFileChecker &LCF; 833 APISet &API; 834 Preprocessor &PP; 835 llvm::SmallVector<PendingMacro> PendingMacros; 836 }; 837 838 } // namespace 839 840 std::unique_ptr<ASTConsumer> 841 ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { 842 OS = CreateOutputFile(CI, InFile); 843 if (!OS) 844 return nullptr; 845 846 ProductName = CI.getFrontendOpts().ProductName; 847 848 // Now that we have enough information about the language options and the 849 // target triple, let's create the APISet before anyone uses it. 850 API = std::make_unique<APISet>( 851 CI.getTarget().getTriple(), 852 CI.getFrontendOpts().Inputs.back().getKind().getLanguage()); 853 854 auto LCF = std::make_unique<LocationFileChecker>(CI, KnownInputFiles); 855 856 CI.getPreprocessor().addPPCallbacks(std::make_unique<MacroCallback>( 857 CI.getSourceManager(), *LCF, *API, CI.getPreprocessor())); 858 859 // Do not include location in anonymous decls. 860 PrintingPolicy Policy = CI.getASTContext().getPrintingPolicy(); 861 Policy.AnonymousTagLocations = false; 862 CI.getASTContext().setPrintingPolicy(Policy); 863 864 if (!CI.getFrontendOpts().ExtractAPIIgnoresFile.empty()) { 865 llvm::handleAllErrors( 866 APIIgnoresList::create(CI.getFrontendOpts().ExtractAPIIgnoresFile, 867 CI.getFileManager()) 868 .moveInto(IgnoresList), 869 [&CI](const IgnoresFileNotFound &Err) { 870 CI.getDiagnostics().Report( 871 diag::err_extract_api_ignores_file_not_found) 872 << Err.Path; 873 }); 874 } 875 876 return std::make_unique<ExtractAPIConsumer>(CI.getASTContext(), 877 std::move(LCF), *API); 878 } 879 880 bool ExtractAPIAction::PrepareToExecuteAction(CompilerInstance &CI) { 881 auto &Inputs = CI.getFrontendOpts().Inputs; 882 if (Inputs.empty()) 883 return true; 884 885 if (!CI.hasFileManager()) 886 if (!CI.createFileManager()) 887 return false; 888 889 auto Kind = Inputs[0].getKind(); 890 891 // Convert the header file inputs into a single input buffer. 892 SmallString<256> HeaderContents; 893 bool IsQuoted = false; 894 for (const FrontendInputFile &FIF : Inputs) { 895 if (Kind.isObjectiveC()) 896 HeaderContents += "#import"; 897 else 898 HeaderContents += "#include"; 899 900 StringRef FilePath = FIF.getFile(); 901 if (auto RelativeName = getRelativeIncludeName(CI, FilePath, &IsQuoted)) { 902 if (IsQuoted) 903 HeaderContents += " \""; 904 else 905 HeaderContents += " <"; 906 907 HeaderContents += *RelativeName; 908 909 if (IsQuoted) 910 HeaderContents += "\"\n"; 911 else 912 HeaderContents += ">\n"; 913 KnownInputFiles.emplace_back(static_cast<SmallString<32>>(*RelativeName), 914 IsQuoted); 915 } else { 916 HeaderContents += " \""; 917 HeaderContents += FilePath; 918 HeaderContents += "\"\n"; 919 KnownInputFiles.emplace_back(FilePath, true); 920 } 921 } 922 923 if (CI.getHeaderSearchOpts().Verbose) 924 CI.getVerboseOutputStream() << getInputBufferName() << ":\n" 925 << HeaderContents << "\n"; 926 927 Buffer = llvm::MemoryBuffer::getMemBufferCopy(HeaderContents, 928 getInputBufferName()); 929 930 // Set that buffer up as our "real" input in the CompilerInstance. 931 Inputs.clear(); 932 Inputs.emplace_back(Buffer->getMemBufferRef(), Kind, /*IsSystem*/ false); 933 934 return true; 935 } 936 937 void ExtractAPIAction::EndSourceFileAction() { 938 if (!OS) 939 return; 940 941 // Setup a SymbolGraphSerializer to write out collected API information in 942 // the Symbol Graph format. 943 // FIXME: Make the kind of APISerializer configurable. 944 SymbolGraphSerializer SGSerializer(*API, ProductName, IgnoresList); 945 SGSerializer.serialize(*OS); 946 OS.reset(); 947 } 948 949 std::unique_ptr<raw_pwrite_stream> 950 ExtractAPIAction::CreateOutputFile(CompilerInstance &CI, StringRef InFile) { 951 std::unique_ptr<raw_pwrite_stream> OS = 952 CI.createDefaultOutputFile(/*Binary=*/false, InFile, /*Extension=*/"json", 953 /*RemoveFileOnSignal=*/false); 954 if (!OS) 955 return nullptr; 956 return OS; 957 } 958