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/MemoryBuffer.h" 42 #include "llvm/Support/raw_ostream.h" 43 #include <memory> 44 #include <utility> 45 46 using namespace clang; 47 using namespace extractapi; 48 49 namespace { 50 51 StringRef getTypedefName(const TagDecl *Decl) { 52 if (const auto *TypedefDecl = Decl->getTypedefNameForAnonDecl()) 53 return TypedefDecl->getName(); 54 55 return {}; 56 } 57 58 struct LocationFileChecker { 59 bool isLocationInKnownFile(SourceLocation Loc) { 60 // If the loc refers to a macro expansion we need to first get the file 61 // location of the expansion. 62 auto FileLoc = SM.getFileLoc(Loc); 63 FileID FID = SM.getFileID(FileLoc); 64 if (FID.isInvalid()) 65 return false; 66 67 const auto *File = SM.getFileEntryForID(FID); 68 if (!File) 69 return false; 70 71 if (KnownFileEntries.count(File)) 72 return true; 73 74 return false; 75 } 76 77 LocationFileChecker(const SourceManager &SM, 78 const std::vector<std::string> &KnownFiles) 79 : SM(SM) { 80 for (const auto &KnownFilePath : KnownFiles) 81 if (auto FileEntry = SM.getFileManager().getFile(KnownFilePath)) 82 KnownFileEntries.insert(*FileEntry); 83 } 84 85 private: 86 const SourceManager &SM; 87 llvm::DenseSet<const FileEntry *> KnownFileEntries; 88 }; 89 90 /// The RecursiveASTVisitor to traverse symbol declarations and collect API 91 /// information. 92 class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> { 93 public: 94 ExtractAPIVisitor(ASTContext &Context, LocationFileChecker &LCF, APISet &API) 95 : Context(Context), API(API), LCF(LCF) {} 96 97 const APISet &getAPI() const { return API; } 98 99 bool VisitVarDecl(const VarDecl *Decl) { 100 // Skip function parameters. 101 if (isa<ParmVarDecl>(Decl)) 102 return true; 103 104 // Skip non-global variables in records (struct/union/class). 105 if (Decl->getDeclContext()->isRecord()) 106 return true; 107 108 // Skip local variables inside function or method. 109 if (!Decl->isDefinedOutsideFunctionOrMethod()) 110 return true; 111 112 // If this is a template but not specialization or instantiation, skip. 113 if (Decl->getASTContext().getTemplateOrSpecializationInfo(Decl) && 114 Decl->getTemplateSpecializationKind() == TSK_Undeclared) 115 return true; 116 117 if (!LCF.isLocationInKnownFile(Decl->getLocation())) 118 return true; 119 120 // Collect symbol information. 121 StringRef Name = Decl->getName(); 122 StringRef USR = API.recordUSR(Decl); 123 PresumedLoc Loc = 124 Context.getSourceManager().getPresumedLoc(Decl->getLocation()); 125 AvailabilityInfo Availability = getAvailability(Decl); 126 LinkageInfo Linkage = Decl->getLinkageAndVisibility(); 127 DocComment Comment; 128 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) 129 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 130 Context.getDiagnostics()); 131 132 // Build declaration fragments and sub-heading for the variable. 133 DeclarationFragments Declaration = 134 DeclarationFragmentsBuilder::getFragmentsForVar(Decl); 135 DeclarationFragments SubHeading = 136 DeclarationFragmentsBuilder::getSubHeading(Decl); 137 138 // Add the global variable record to the API set. 139 API.addGlobalVar(Name, USR, Loc, Availability, Linkage, Comment, 140 Declaration, SubHeading); 141 return true; 142 } 143 144 bool VisitFunctionDecl(const FunctionDecl *Decl) { 145 if (const auto *Method = dyn_cast<CXXMethodDecl>(Decl)) { 146 // Skip member function in class templates. 147 if (Method->getParent()->getDescribedClassTemplate() != nullptr) 148 return true; 149 150 // Skip methods in records. 151 for (auto P : Context.getParents(*Method)) { 152 if (P.get<CXXRecordDecl>()) 153 return true; 154 } 155 156 // Skip ConstructorDecl and DestructorDecl. 157 if (isa<CXXConstructorDecl>(Method) || isa<CXXDestructorDecl>(Method)) 158 return true; 159 } 160 161 // Skip templated functions. 162 switch (Decl->getTemplatedKind()) { 163 case FunctionDecl::TK_NonTemplate: 164 break; 165 case FunctionDecl::TK_MemberSpecialization: 166 case FunctionDecl::TK_FunctionTemplateSpecialization: 167 if (auto *TemplateInfo = Decl->getTemplateSpecializationInfo()) { 168 if (!TemplateInfo->isExplicitInstantiationOrSpecialization()) 169 return true; 170 } 171 break; 172 case FunctionDecl::TK_FunctionTemplate: 173 case FunctionDecl::TK_DependentFunctionTemplateSpecialization: 174 return true; 175 } 176 177 if (!LCF.isLocationInKnownFile(Decl->getLocation())) 178 return true; 179 180 // Collect symbol information. 181 StringRef Name = Decl->getName(); 182 StringRef USR = API.recordUSR(Decl); 183 PresumedLoc Loc = 184 Context.getSourceManager().getPresumedLoc(Decl->getLocation()); 185 AvailabilityInfo Availability = getAvailability(Decl); 186 LinkageInfo Linkage = Decl->getLinkageAndVisibility(); 187 DocComment Comment; 188 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) 189 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 190 Context.getDiagnostics()); 191 192 // Build declaration fragments, sub-heading, and signature of the function. 193 DeclarationFragments Declaration = 194 DeclarationFragmentsBuilder::getFragmentsForFunction(Decl); 195 DeclarationFragments SubHeading = 196 DeclarationFragmentsBuilder::getSubHeading(Decl); 197 FunctionSignature Signature = 198 DeclarationFragmentsBuilder::getFunctionSignature(Decl); 199 200 // Add the function record to the API set. 201 API.addFunction(Name, USR, Loc, Availability, Linkage, Comment, Declaration, 202 SubHeading, Signature); 203 return true; 204 } 205 206 bool VisitEnumDecl(const EnumDecl *Decl) { 207 if (!Decl->isComplete()) 208 return true; 209 210 // Skip forward declaration. 211 if (!Decl->isThisDeclarationADefinition()) 212 return true; 213 214 if (!LCF.isLocationInKnownFile(Decl->getLocation())) 215 return true; 216 217 // Collect symbol information. 218 StringRef Name = Decl->getName(); 219 if (Name.empty()) 220 Name = getTypedefName(Decl); 221 StringRef USR = API.recordUSR(Decl); 222 PresumedLoc Loc = 223 Context.getSourceManager().getPresumedLoc(Decl->getLocation()); 224 AvailabilityInfo Availability = getAvailability(Decl); 225 DocComment Comment; 226 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) 227 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 228 Context.getDiagnostics()); 229 230 // Build declaration fragments and sub-heading for the enum. 231 DeclarationFragments Declaration = 232 DeclarationFragmentsBuilder::getFragmentsForEnum(Decl); 233 DeclarationFragments SubHeading = 234 DeclarationFragmentsBuilder::getSubHeading(Decl); 235 236 EnumRecord *EnumRecord = API.addEnum(Name, USR, Loc, Availability, Comment, 237 Declaration, SubHeading); 238 239 // Now collect information about the enumerators in this enum. 240 recordEnumConstants(EnumRecord, Decl->enumerators()); 241 242 return true; 243 } 244 245 bool VisitRecordDecl(const RecordDecl *Decl) { 246 if (!Decl->isCompleteDefinition()) 247 return true; 248 249 // Skip C++ structs/classes/unions 250 // TODO: support C++ records 251 if (isa<CXXRecordDecl>(Decl)) 252 return true; 253 254 if (!LCF.isLocationInKnownFile(Decl->getLocation())) 255 return true; 256 257 // Collect symbol information. 258 StringRef Name = Decl->getName(); 259 if (Name.empty()) 260 Name = getTypedefName(Decl); 261 StringRef USR = API.recordUSR(Decl); 262 PresumedLoc Loc = 263 Context.getSourceManager().getPresumedLoc(Decl->getLocation()); 264 AvailabilityInfo Availability = getAvailability(Decl); 265 DocComment Comment; 266 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) 267 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 268 Context.getDiagnostics()); 269 270 // Build declaration fragments and sub-heading for the struct. 271 DeclarationFragments Declaration = 272 DeclarationFragmentsBuilder::getFragmentsForStruct(Decl); 273 DeclarationFragments SubHeading = 274 DeclarationFragmentsBuilder::getSubHeading(Decl); 275 276 StructRecord *StructRecord = API.addStruct( 277 Name, USR, Loc, Availability, Comment, Declaration, SubHeading); 278 279 // Now collect information about the fields in this struct. 280 recordStructFields(StructRecord, Decl->fields()); 281 282 return true; 283 } 284 285 bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *Decl) { 286 // Skip forward declaration for classes (@class) 287 if (!Decl->isThisDeclarationADefinition()) 288 return true; 289 290 if (!LCF.isLocationInKnownFile(Decl->getLocation())) 291 return true; 292 293 // Collect symbol information. 294 StringRef Name = Decl->getName(); 295 StringRef USR = API.recordUSR(Decl); 296 PresumedLoc Loc = 297 Context.getSourceManager().getPresumedLoc(Decl->getLocation()); 298 AvailabilityInfo Availability = getAvailability(Decl); 299 LinkageInfo Linkage = Decl->getLinkageAndVisibility(); 300 DocComment Comment; 301 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) 302 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 303 Context.getDiagnostics()); 304 305 // Build declaration fragments and sub-heading for the interface. 306 DeclarationFragments Declaration = 307 DeclarationFragmentsBuilder::getFragmentsForObjCInterface(Decl); 308 DeclarationFragments SubHeading = 309 DeclarationFragmentsBuilder::getSubHeading(Decl); 310 311 // Collect super class information. 312 SymbolReference SuperClass; 313 if (const auto *SuperClassDecl = Decl->getSuperClass()) { 314 SuperClass.Name = SuperClassDecl->getObjCRuntimeNameAsString(); 315 SuperClass.USR = API.recordUSR(SuperClassDecl); 316 } 317 318 ObjCInterfaceRecord *ObjCInterfaceRecord = 319 API.addObjCInterface(Name, USR, Loc, Availability, Linkage, Comment, 320 Declaration, SubHeading, SuperClass); 321 322 // Record all methods (selectors). This doesn't include automatically 323 // synthesized property methods. 324 recordObjCMethods(ObjCInterfaceRecord, Decl->methods()); 325 recordObjCProperties(ObjCInterfaceRecord, Decl->properties()); 326 recordObjCInstanceVariables(ObjCInterfaceRecord, Decl->ivars()); 327 recordObjCProtocols(ObjCInterfaceRecord, Decl->protocols()); 328 329 return true; 330 } 331 332 bool VisitObjCProtocolDecl(const ObjCProtocolDecl *Decl) { 333 // Skip forward declaration for protocols (@protocol). 334 if (!Decl->isThisDeclarationADefinition()) 335 return true; 336 337 if (!LCF.isLocationInKnownFile(Decl->getLocation())) 338 return true; 339 340 // Collect symbol information. 341 StringRef Name = Decl->getName(); 342 StringRef USR = API.recordUSR(Decl); 343 PresumedLoc Loc = 344 Context.getSourceManager().getPresumedLoc(Decl->getLocation()); 345 AvailabilityInfo Availability = getAvailability(Decl); 346 DocComment Comment; 347 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) 348 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 349 Context.getDiagnostics()); 350 351 // Build declaration fragments and sub-heading for the protocol. 352 DeclarationFragments Declaration = 353 DeclarationFragmentsBuilder::getFragmentsForObjCProtocol(Decl); 354 DeclarationFragments SubHeading = 355 DeclarationFragmentsBuilder::getSubHeading(Decl); 356 357 ObjCProtocolRecord *ObjCProtocolRecord = API.addObjCProtocol( 358 Name, USR, Loc, Availability, Comment, Declaration, SubHeading); 359 360 recordObjCMethods(ObjCProtocolRecord, Decl->methods()); 361 recordObjCProperties(ObjCProtocolRecord, Decl->properties()); 362 recordObjCProtocols(ObjCProtocolRecord, Decl->protocols()); 363 364 return true; 365 } 366 367 bool VisitTypedefNameDecl(const TypedefNameDecl *Decl) { 368 // Skip ObjC Type Parameter for now. 369 if (isa<ObjCTypeParamDecl>(Decl)) 370 return true; 371 372 if (!Decl->isDefinedOutsideFunctionOrMethod()) 373 return true; 374 375 if (!LCF.isLocationInKnownFile(Decl->getLocation())) 376 return true; 377 378 PresumedLoc Loc = 379 Context.getSourceManager().getPresumedLoc(Decl->getLocation()); 380 StringRef Name = Decl->getName(); 381 AvailabilityInfo Availability = getAvailability(Decl); 382 StringRef USR = API.recordUSR(Decl); 383 DocComment Comment; 384 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) 385 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 386 Context.getDiagnostics()); 387 388 QualType Type = Decl->getUnderlyingType(); 389 SymbolReference SymRef = 390 TypedefUnderlyingTypeResolver(Context).getSymbolReferenceForType(Type, 391 API); 392 393 API.addTypedef(Name, USR, Loc, Availability, Comment, 394 DeclarationFragmentsBuilder::getFragmentsForTypedef(Decl), 395 DeclarationFragmentsBuilder::getSubHeading(Decl), SymRef); 396 397 return true; 398 } 399 400 bool VisitObjCCategoryDecl(const ObjCCategoryDecl *Decl) { 401 // Collect symbol information. 402 StringRef Name = Decl->getName(); 403 StringRef USR = API.recordUSR(Decl); 404 PresumedLoc Loc = 405 Context.getSourceManager().getPresumedLoc(Decl->getLocation()); 406 AvailabilityInfo Availability = getAvailability(Decl); 407 DocComment Comment; 408 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) 409 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 410 Context.getDiagnostics()); 411 // Build declaration fragments and sub-heading for the category. 412 DeclarationFragments Declaration = 413 DeclarationFragmentsBuilder::getFragmentsForObjCCategory(Decl); 414 DeclarationFragments SubHeading = 415 DeclarationFragmentsBuilder::getSubHeading(Decl); 416 417 const ObjCInterfaceDecl *InterfaceDecl = Decl->getClassInterface(); 418 SymbolReference Interface(InterfaceDecl->getName(), 419 API.recordUSR(InterfaceDecl)); 420 421 ObjCCategoryRecord *ObjCCategoryRecord = 422 API.addObjCCategory(Name, USR, Loc, Availability, Comment, Declaration, 423 SubHeading, Interface); 424 425 recordObjCMethods(ObjCCategoryRecord, Decl->methods()); 426 recordObjCProperties(ObjCCategoryRecord, Decl->properties()); 427 recordObjCInstanceVariables(ObjCCategoryRecord, Decl->ivars()); 428 recordObjCProtocols(ObjCCategoryRecord, Decl->protocols()); 429 430 return true; 431 } 432 433 private: 434 /// Get availability information of the declaration \p D. 435 AvailabilityInfo getAvailability(const Decl *D) const { 436 StringRef PlatformName = Context.getTargetInfo().getPlatformName(); 437 438 AvailabilityInfo Availability; 439 // Collect availability attributes from all redeclarations. 440 for (const auto *RD : D->redecls()) { 441 for (const auto *A : RD->specific_attrs<AvailabilityAttr>()) { 442 if (A->getPlatform()->getName() != PlatformName) 443 continue; 444 Availability = AvailabilityInfo(A->getIntroduced(), A->getDeprecated(), 445 A->getObsoleted(), A->getUnavailable(), 446 /* UnconditionallyDeprecated */ false, 447 /* UnconditionallyUnavailable */ false); 448 break; 449 } 450 451 if (const auto *A = RD->getAttr<UnavailableAttr>()) 452 if (!A->isImplicit()) { 453 Availability.Unavailable = true; 454 Availability.UnconditionallyUnavailable = true; 455 } 456 457 if (const auto *A = RD->getAttr<DeprecatedAttr>()) 458 if (!A->isImplicit()) 459 Availability.UnconditionallyDeprecated = true; 460 } 461 462 return Availability; 463 } 464 465 /// Collect API information for the enum constants and associate with the 466 /// parent enum. 467 void recordEnumConstants(EnumRecord *EnumRecord, 468 const EnumDecl::enumerator_range Constants) { 469 for (const auto *Constant : Constants) { 470 // Collect symbol information. 471 StringRef Name = Constant->getName(); 472 StringRef USR = API.recordUSR(Constant); 473 PresumedLoc Loc = 474 Context.getSourceManager().getPresumedLoc(Constant->getLocation()); 475 AvailabilityInfo Availability = getAvailability(Constant); 476 DocComment Comment; 477 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Constant)) 478 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 479 Context.getDiagnostics()); 480 481 // Build declaration fragments and sub-heading for the enum constant. 482 DeclarationFragments Declaration = 483 DeclarationFragmentsBuilder::getFragmentsForEnumConstant(Constant); 484 DeclarationFragments SubHeading = 485 DeclarationFragmentsBuilder::getSubHeading(Constant); 486 487 API.addEnumConstant(EnumRecord, Name, USR, Loc, Availability, Comment, 488 Declaration, SubHeading); 489 } 490 } 491 492 /// Collect API information for the struct fields and associate with the 493 /// parent struct. 494 void recordStructFields(StructRecord *StructRecord, 495 const RecordDecl::field_range Fields) { 496 for (const auto *Field : Fields) { 497 // Collect symbol information. 498 StringRef Name = Field->getName(); 499 StringRef USR = API.recordUSR(Field); 500 PresumedLoc Loc = 501 Context.getSourceManager().getPresumedLoc(Field->getLocation()); 502 AvailabilityInfo Availability = getAvailability(Field); 503 DocComment Comment; 504 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Field)) 505 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 506 Context.getDiagnostics()); 507 508 // Build declaration fragments and sub-heading for the struct field. 509 DeclarationFragments Declaration = 510 DeclarationFragmentsBuilder::getFragmentsForField(Field); 511 DeclarationFragments SubHeading = 512 DeclarationFragmentsBuilder::getSubHeading(Field); 513 514 API.addStructField(StructRecord, Name, USR, Loc, Availability, Comment, 515 Declaration, SubHeading); 516 } 517 } 518 519 /// Collect API information for the Objective-C methods and associate with the 520 /// parent container. 521 void recordObjCMethods(ObjCContainerRecord *Container, 522 const ObjCContainerDecl::method_range Methods) { 523 for (const auto *Method : Methods) { 524 // Don't record selectors for properties. 525 if (Method->isPropertyAccessor()) 526 continue; 527 528 StringRef Name = API.copyString(Method->getSelector().getAsString()); 529 StringRef USR = API.recordUSR(Method); 530 PresumedLoc Loc = 531 Context.getSourceManager().getPresumedLoc(Method->getLocation()); 532 AvailabilityInfo Availability = getAvailability(Method); 533 DocComment Comment; 534 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Method)) 535 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 536 Context.getDiagnostics()); 537 538 // Build declaration fragments, sub-heading, and signature for the method. 539 DeclarationFragments Declaration = 540 DeclarationFragmentsBuilder::getFragmentsForObjCMethod(Method); 541 DeclarationFragments SubHeading = 542 DeclarationFragmentsBuilder::getSubHeading(Method); 543 FunctionSignature Signature = 544 DeclarationFragmentsBuilder::getFunctionSignature(Method); 545 546 API.addObjCMethod(Container, Name, USR, Loc, Availability, Comment, 547 Declaration, SubHeading, Signature, 548 Method->isInstanceMethod()); 549 } 550 } 551 552 void recordObjCProperties(ObjCContainerRecord *Container, 553 const ObjCContainerDecl::prop_range Properties) { 554 for (const auto *Property : Properties) { 555 StringRef Name = Property->getName(); 556 StringRef USR = API.recordUSR(Property); 557 PresumedLoc Loc = 558 Context.getSourceManager().getPresumedLoc(Property->getLocation()); 559 AvailabilityInfo Availability = getAvailability(Property); 560 DocComment Comment; 561 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Property)) 562 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 563 Context.getDiagnostics()); 564 565 // Build declaration fragments and sub-heading for the property. 566 DeclarationFragments Declaration = 567 DeclarationFragmentsBuilder::getFragmentsForObjCProperty(Property); 568 DeclarationFragments SubHeading = 569 DeclarationFragmentsBuilder::getSubHeading(Property); 570 571 StringRef GetterName = 572 API.copyString(Property->getGetterName().getAsString()); 573 StringRef SetterName = 574 API.copyString(Property->getSetterName().getAsString()); 575 576 // Get the attributes for property. 577 unsigned Attributes = ObjCPropertyRecord::NoAttr; 578 if (Property->getPropertyAttributes() & 579 ObjCPropertyAttribute::kind_readonly) 580 Attributes |= ObjCPropertyRecord::ReadOnly; 581 if (Property->getPropertyAttributes() & ObjCPropertyAttribute::kind_class) 582 Attributes |= ObjCPropertyRecord::Class; 583 584 API.addObjCProperty( 585 Container, Name, USR, Loc, Availability, Comment, Declaration, 586 SubHeading, 587 static_cast<ObjCPropertyRecord::AttributeKind>(Attributes), 588 GetterName, SetterName, Property->isOptional()); 589 } 590 } 591 592 void recordObjCInstanceVariables( 593 ObjCContainerRecord *Container, 594 const llvm::iterator_range< 595 DeclContext::specific_decl_iterator<ObjCIvarDecl>> 596 Ivars) { 597 for (const auto *Ivar : Ivars) { 598 StringRef Name = Ivar->getName(); 599 StringRef USR = API.recordUSR(Ivar); 600 PresumedLoc Loc = 601 Context.getSourceManager().getPresumedLoc(Ivar->getLocation()); 602 AvailabilityInfo Availability = getAvailability(Ivar); 603 DocComment Comment; 604 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Ivar)) 605 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 606 Context.getDiagnostics()); 607 608 // Build declaration fragments and sub-heading for the instance variable. 609 DeclarationFragments Declaration = 610 DeclarationFragmentsBuilder::getFragmentsForField(Ivar); 611 DeclarationFragments SubHeading = 612 DeclarationFragmentsBuilder::getSubHeading(Ivar); 613 614 ObjCInstanceVariableRecord::AccessControl Access = 615 Ivar->getCanonicalAccessControl(); 616 617 API.addObjCInstanceVariable(Container, Name, USR, Loc, Availability, 618 Comment, Declaration, SubHeading, Access); 619 } 620 } 621 622 void recordObjCProtocols(ObjCContainerRecord *Container, 623 ObjCInterfaceDecl::protocol_range Protocols) { 624 for (const auto *Protocol : Protocols) 625 Container->Protocols.emplace_back(Protocol->getName(), 626 API.recordUSR(Protocol)); 627 } 628 629 ASTContext &Context; 630 APISet &API; 631 LocationFileChecker &LCF; 632 }; 633 634 class ExtractAPIConsumer : public ASTConsumer { 635 public: 636 ExtractAPIConsumer(ASTContext &Context, 637 std::unique_ptr<LocationFileChecker> LCF, APISet &API) 638 : Visitor(Context, *LCF, API), LCF(std::move(LCF)) {} 639 640 void HandleTranslationUnit(ASTContext &Context) override { 641 // Use ExtractAPIVisitor to traverse symbol declarations in the context. 642 Visitor.TraverseDecl(Context.getTranslationUnitDecl()); 643 } 644 645 private: 646 ExtractAPIVisitor Visitor; 647 std::unique_ptr<LocationFileChecker> LCF; 648 }; 649 650 class MacroCallback : public PPCallbacks { 651 public: 652 MacroCallback(const SourceManager &SM, LocationFileChecker &LCF, APISet &API) 653 : SM(SM), LCF(LCF), API(API) {} 654 655 void MacroDefined(const Token &MacroNameToken, 656 const MacroDirective *MD) override { 657 auto *MacroInfo = MD->getMacroInfo(); 658 659 if (MacroInfo->isBuiltinMacro()) 660 return; 661 662 auto SourceLoc = MacroNameToken.getLocation(); 663 if (SM.isWrittenInBuiltinFile(SourceLoc) || 664 SM.isWrittenInCommandLineFile(SourceLoc)) 665 return; 666 667 PendingMacros.emplace_back(MacroNameToken, MD); 668 } 669 670 // If a macro gets undefined at some point during preprocessing of the inputs 671 // it means that it isn't an exposed API and we should therefore not add a 672 // macro definition for it. 673 void MacroUndefined(const Token &MacroNameToken, const MacroDefinition &MD, 674 const MacroDirective *Undef) override { 675 // If this macro wasn't previously defined we don't need to do anything 676 // here. 677 if (!Undef) 678 return; 679 680 llvm::erase_if(PendingMacros, [&MD](const PendingMacro &PM) { 681 return MD.getMacroInfo()->getDefinitionLoc() == 682 PM.MD->getMacroInfo()->getDefinitionLoc(); 683 }); 684 } 685 686 void EndOfMainFile() override { 687 for (auto &PM : PendingMacros) { 688 // `isUsedForHeaderGuard` is only set when the preprocessor leaves the 689 // file so check for it here. 690 if (PM.MD->getMacroInfo()->isUsedForHeaderGuard()) 691 continue; 692 693 if (!LCF.isLocationInKnownFile(PM.MacroNameToken.getLocation())) 694 continue; 695 696 StringRef Name = PM.MacroNameToken.getIdentifierInfo()->getName(); 697 PresumedLoc Loc = SM.getPresumedLoc(PM.MacroNameToken.getLocation()); 698 StringRef USR = 699 API.recordUSRForMacro(Name, PM.MacroNameToken.getLocation(), SM); 700 701 API.addMacroDefinition( 702 Name, USR, Loc, 703 DeclarationFragmentsBuilder::getFragmentsForMacro(Name, PM.MD), 704 DeclarationFragmentsBuilder::getSubHeadingForMacro(Name)); 705 } 706 707 PendingMacros.clear(); 708 } 709 710 private: 711 struct PendingMacro { 712 Token MacroNameToken; 713 const MacroDirective *MD; 714 715 PendingMacro(const Token &MacroNameToken, const MacroDirective *MD) 716 : MacroNameToken(MacroNameToken), MD(MD) {} 717 }; 718 719 const SourceManager &SM; 720 LocationFileChecker &LCF; 721 APISet &API; 722 llvm::SmallVector<PendingMacro> PendingMacros; 723 }; 724 725 } // namespace 726 727 std::unique_ptr<ASTConsumer> 728 ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { 729 OS = CreateOutputFile(CI, InFile); 730 if (!OS) 731 return nullptr; 732 733 ProductName = CI.getFrontendOpts().ProductName; 734 735 // Now that we have enough information about the language options and the 736 // target triple, let's create the APISet before anyone uses it. 737 API = std::make_unique<APISet>( 738 CI.getTarget().getTriple(), 739 CI.getFrontendOpts().Inputs.back().getKind().getLanguage()); 740 741 auto LCF = std::make_unique<LocationFileChecker>(CI.getSourceManager(), 742 KnownInputFiles); 743 744 // Register preprocessor callbacks that will add macro definitions to API. 745 CI.getPreprocessor().addPPCallbacks( 746 std::make_unique<MacroCallback>(CI.getSourceManager(), *LCF, *API)); 747 748 return std::make_unique<ExtractAPIConsumer>(CI.getASTContext(), 749 std::move(LCF), *API); 750 } 751 752 bool ExtractAPIAction::PrepareToExecuteAction(CompilerInstance &CI) { 753 auto &Inputs = CI.getFrontendOpts().Inputs; 754 if (Inputs.empty()) 755 return true; 756 757 auto Kind = Inputs[0].getKind(); 758 759 // Convert the header file inputs into a single input buffer. 760 SmallString<256> HeaderContents; 761 for (const FrontendInputFile &FIF : Inputs) { 762 if (Kind.isObjectiveC()) 763 HeaderContents += "#import"; 764 else 765 HeaderContents += "#include"; 766 HeaderContents += " \""; 767 HeaderContents += FIF.getFile(); 768 HeaderContents += "\"\n"; 769 770 KnownInputFiles.emplace_back(FIF.getFile()); 771 } 772 773 Buffer = llvm::MemoryBuffer::getMemBufferCopy(HeaderContents, 774 getInputBufferName()); 775 776 // Set that buffer up as our "real" input in the CompilerInstance. 777 Inputs.clear(); 778 Inputs.emplace_back(Buffer->getMemBufferRef(), Kind, /*IsSystem*/ false); 779 780 return true; 781 } 782 783 void ExtractAPIAction::EndSourceFileAction() { 784 if (!OS) 785 return; 786 787 // Setup a SymbolGraphSerializer to write out collected API information in 788 // the Symbol Graph format. 789 // FIXME: Make the kind of APISerializer configurable. 790 SymbolGraphSerializer SGSerializer(*API, ProductName); 791 SGSerializer.serialize(*OS); 792 OS->flush(); 793 } 794 795 std::unique_ptr<raw_pwrite_stream> 796 ExtractAPIAction::CreateOutputFile(CompilerInstance &CI, StringRef InFile) { 797 std::unique_ptr<raw_pwrite_stream> OS = 798 CI.createDefaultOutputFile(/*Binary=*/false, InFile, /*Extension=*/"json", 799 /*RemoveFileOnSignal=*/false); 800 if (!OS) 801 return nullptr; 802 return OS; 803 } 804