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.addGlobalFunction(Name, USR, Loc, Availability, Linkage, Comment, 202 Declaration, 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 std::string NameString = Decl->getQualifiedNameAsString(); 219 StringRef Name(NameString); 220 if (Name.empty()) 221 Name = getTypedefName(Decl); 222 223 StringRef USR = API.recordUSR(Decl); 224 PresumedLoc Loc = 225 Context.getSourceManager().getPresumedLoc(Decl->getLocation()); 226 AvailabilityInfo Availability = getAvailability(Decl); 227 DocComment Comment; 228 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) 229 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 230 Context.getDiagnostics()); 231 232 // Build declaration fragments and sub-heading for the enum. 233 DeclarationFragments Declaration = 234 DeclarationFragmentsBuilder::getFragmentsForEnum(Decl); 235 DeclarationFragments SubHeading = 236 DeclarationFragmentsBuilder::getSubHeading(Decl); 237 238 EnumRecord *EnumRecord = 239 API.addEnum(API.copyString(Name), USR, Loc, Availability, Comment, 240 Declaration, SubHeading); 241 242 // Now collect information about the enumerators in this enum. 243 recordEnumConstants(EnumRecord, Decl->enumerators()); 244 245 return true; 246 } 247 248 bool VisitRecordDecl(const RecordDecl *Decl) { 249 if (!Decl->isCompleteDefinition()) 250 return true; 251 252 // Skip C++ structs/classes/unions 253 // TODO: support C++ records 254 if (isa<CXXRecordDecl>(Decl)) 255 return true; 256 257 if (!LCF.isLocationInKnownFile(Decl->getLocation())) 258 return true; 259 260 // Collect symbol information. 261 StringRef Name = Decl->getName(); 262 if (Name.empty()) 263 Name = getTypedefName(Decl); 264 StringRef USR = API.recordUSR(Decl); 265 PresumedLoc Loc = 266 Context.getSourceManager().getPresumedLoc(Decl->getLocation()); 267 AvailabilityInfo Availability = getAvailability(Decl); 268 DocComment Comment; 269 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) 270 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 271 Context.getDiagnostics()); 272 273 // Build declaration fragments and sub-heading for the struct. 274 DeclarationFragments Declaration = 275 DeclarationFragmentsBuilder::getFragmentsForStruct(Decl); 276 DeclarationFragments SubHeading = 277 DeclarationFragmentsBuilder::getSubHeading(Decl); 278 279 StructRecord *StructRecord = API.addStruct( 280 Name, USR, Loc, Availability, Comment, Declaration, SubHeading); 281 282 // Now collect information about the fields in this struct. 283 recordStructFields(StructRecord, Decl->fields()); 284 285 return true; 286 } 287 288 bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *Decl) { 289 // Skip forward declaration for classes (@class) 290 if (!Decl->isThisDeclarationADefinition()) 291 return true; 292 293 if (!LCF.isLocationInKnownFile(Decl->getLocation())) 294 return true; 295 296 // Collect symbol information. 297 StringRef Name = Decl->getName(); 298 StringRef USR = API.recordUSR(Decl); 299 PresumedLoc Loc = 300 Context.getSourceManager().getPresumedLoc(Decl->getLocation()); 301 AvailabilityInfo Availability = getAvailability(Decl); 302 LinkageInfo Linkage = Decl->getLinkageAndVisibility(); 303 DocComment Comment; 304 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) 305 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 306 Context.getDiagnostics()); 307 308 // Build declaration fragments and sub-heading for the interface. 309 DeclarationFragments Declaration = 310 DeclarationFragmentsBuilder::getFragmentsForObjCInterface(Decl); 311 DeclarationFragments SubHeading = 312 DeclarationFragmentsBuilder::getSubHeading(Decl); 313 314 // Collect super class information. 315 SymbolReference SuperClass; 316 if (const auto *SuperClassDecl = Decl->getSuperClass()) { 317 SuperClass.Name = SuperClassDecl->getObjCRuntimeNameAsString(); 318 SuperClass.USR = API.recordUSR(SuperClassDecl); 319 } 320 321 ObjCInterfaceRecord *ObjCInterfaceRecord = 322 API.addObjCInterface(Name, USR, Loc, Availability, Linkage, Comment, 323 Declaration, SubHeading, SuperClass); 324 325 // Record all methods (selectors). This doesn't include automatically 326 // synthesized property methods. 327 recordObjCMethods(ObjCInterfaceRecord, Decl->methods()); 328 recordObjCProperties(ObjCInterfaceRecord, Decl->properties()); 329 recordObjCInstanceVariables(ObjCInterfaceRecord, Decl->ivars()); 330 recordObjCProtocols(ObjCInterfaceRecord, Decl->protocols()); 331 332 return true; 333 } 334 335 bool VisitObjCProtocolDecl(const ObjCProtocolDecl *Decl) { 336 // Skip forward declaration for protocols (@protocol). 337 if (!Decl->isThisDeclarationADefinition()) 338 return true; 339 340 if (!LCF.isLocationInKnownFile(Decl->getLocation())) 341 return true; 342 343 // Collect symbol information. 344 StringRef Name = Decl->getName(); 345 StringRef USR = API.recordUSR(Decl); 346 PresumedLoc Loc = 347 Context.getSourceManager().getPresumedLoc(Decl->getLocation()); 348 AvailabilityInfo Availability = getAvailability(Decl); 349 DocComment Comment; 350 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) 351 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 352 Context.getDiagnostics()); 353 354 // Build declaration fragments and sub-heading for the protocol. 355 DeclarationFragments Declaration = 356 DeclarationFragmentsBuilder::getFragmentsForObjCProtocol(Decl); 357 DeclarationFragments SubHeading = 358 DeclarationFragmentsBuilder::getSubHeading(Decl); 359 360 ObjCProtocolRecord *ObjCProtocolRecord = API.addObjCProtocol( 361 Name, USR, Loc, Availability, Comment, Declaration, SubHeading); 362 363 recordObjCMethods(ObjCProtocolRecord, Decl->methods()); 364 recordObjCProperties(ObjCProtocolRecord, Decl->properties()); 365 recordObjCProtocols(ObjCProtocolRecord, Decl->protocols()); 366 367 return true; 368 } 369 370 bool VisitTypedefNameDecl(const TypedefNameDecl *Decl) { 371 // Skip ObjC Type Parameter for now. 372 if (isa<ObjCTypeParamDecl>(Decl)) 373 return true; 374 375 if (!Decl->isDefinedOutsideFunctionOrMethod()) 376 return true; 377 378 if (!LCF.isLocationInKnownFile(Decl->getLocation())) 379 return true; 380 381 PresumedLoc Loc = 382 Context.getSourceManager().getPresumedLoc(Decl->getLocation()); 383 StringRef Name = Decl->getName(); 384 AvailabilityInfo Availability = getAvailability(Decl); 385 StringRef USR = API.recordUSR(Decl); 386 DocComment Comment; 387 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) 388 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 389 Context.getDiagnostics()); 390 391 QualType Type = Decl->getUnderlyingType(); 392 SymbolReference SymRef = 393 TypedefUnderlyingTypeResolver(Context).getSymbolReferenceForType(Type, 394 API); 395 396 API.addTypedef(Name, USR, Loc, Availability, Comment, 397 DeclarationFragmentsBuilder::getFragmentsForTypedef(Decl), 398 DeclarationFragmentsBuilder::getSubHeading(Decl), SymRef); 399 400 return true; 401 } 402 403 bool VisitObjCCategoryDecl(const ObjCCategoryDecl *Decl) { 404 // Collect symbol information. 405 StringRef Name = Decl->getName(); 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 // Build declaration fragments and sub-heading for the category. 415 DeclarationFragments Declaration = 416 DeclarationFragmentsBuilder::getFragmentsForObjCCategory(Decl); 417 DeclarationFragments SubHeading = 418 DeclarationFragmentsBuilder::getSubHeading(Decl); 419 420 const ObjCInterfaceDecl *InterfaceDecl = Decl->getClassInterface(); 421 SymbolReference Interface(InterfaceDecl->getName(), 422 API.recordUSR(InterfaceDecl)); 423 424 ObjCCategoryRecord *ObjCCategoryRecord = 425 API.addObjCCategory(Name, USR, Loc, Availability, Comment, Declaration, 426 SubHeading, Interface); 427 428 recordObjCMethods(ObjCCategoryRecord, Decl->methods()); 429 recordObjCProperties(ObjCCategoryRecord, Decl->properties()); 430 recordObjCInstanceVariables(ObjCCategoryRecord, Decl->ivars()); 431 recordObjCProtocols(ObjCCategoryRecord, Decl->protocols()); 432 433 return true; 434 } 435 436 private: 437 /// Get availability information of the declaration \p D. 438 AvailabilityInfo getAvailability(const Decl *D) const { 439 StringRef PlatformName = Context.getTargetInfo().getPlatformName(); 440 441 AvailabilityInfo Availability; 442 // Collect availability attributes from all redeclarations. 443 for (const auto *RD : D->redecls()) { 444 for (const auto *A : RD->specific_attrs<AvailabilityAttr>()) { 445 if (A->getPlatform()->getName() != PlatformName) 446 continue; 447 Availability = AvailabilityInfo(A->getIntroduced(), A->getDeprecated(), 448 A->getObsoleted(), A->getUnavailable(), 449 /* UnconditionallyDeprecated */ false, 450 /* UnconditionallyUnavailable */ false); 451 break; 452 } 453 454 if (const auto *A = RD->getAttr<UnavailableAttr>()) 455 if (!A->isImplicit()) { 456 Availability.Unavailable = true; 457 Availability.UnconditionallyUnavailable = true; 458 } 459 460 if (const auto *A = RD->getAttr<DeprecatedAttr>()) 461 if (!A->isImplicit()) 462 Availability.UnconditionallyDeprecated = true; 463 } 464 465 return Availability; 466 } 467 468 /// Collect API information for the enum constants and associate with the 469 /// parent enum. 470 void recordEnumConstants(EnumRecord *EnumRecord, 471 const EnumDecl::enumerator_range Constants) { 472 for (const auto *Constant : Constants) { 473 // Collect symbol information. 474 StringRef Name = Constant->getName(); 475 StringRef USR = API.recordUSR(Constant); 476 PresumedLoc Loc = 477 Context.getSourceManager().getPresumedLoc(Constant->getLocation()); 478 AvailabilityInfo Availability = getAvailability(Constant); 479 DocComment Comment; 480 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Constant)) 481 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 482 Context.getDiagnostics()); 483 484 // Build declaration fragments and sub-heading for the enum constant. 485 DeclarationFragments Declaration = 486 DeclarationFragmentsBuilder::getFragmentsForEnumConstant(Constant); 487 DeclarationFragments SubHeading = 488 DeclarationFragmentsBuilder::getSubHeading(Constant); 489 490 API.addEnumConstant(EnumRecord, Name, USR, Loc, Availability, Comment, 491 Declaration, SubHeading); 492 } 493 } 494 495 /// Collect API information for the struct fields and associate with the 496 /// parent struct. 497 void recordStructFields(StructRecord *StructRecord, 498 const RecordDecl::field_range Fields) { 499 for (const auto *Field : Fields) { 500 // Collect symbol information. 501 StringRef Name = Field->getName(); 502 StringRef USR = API.recordUSR(Field); 503 PresumedLoc Loc = 504 Context.getSourceManager().getPresumedLoc(Field->getLocation()); 505 AvailabilityInfo Availability = getAvailability(Field); 506 DocComment Comment; 507 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Field)) 508 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 509 Context.getDiagnostics()); 510 511 // Build declaration fragments and sub-heading for the struct field. 512 DeclarationFragments Declaration = 513 DeclarationFragmentsBuilder::getFragmentsForField(Field); 514 DeclarationFragments SubHeading = 515 DeclarationFragmentsBuilder::getSubHeading(Field); 516 517 API.addStructField(StructRecord, Name, USR, Loc, Availability, Comment, 518 Declaration, SubHeading); 519 } 520 } 521 522 /// Collect API information for the Objective-C methods and associate with the 523 /// parent container. 524 void recordObjCMethods(ObjCContainerRecord *Container, 525 const ObjCContainerDecl::method_range Methods) { 526 for (const auto *Method : Methods) { 527 // Don't record selectors for properties. 528 if (Method->isPropertyAccessor()) 529 continue; 530 531 StringRef Name = API.copyString(Method->getSelector().getAsString()); 532 StringRef USR = API.recordUSR(Method); 533 PresumedLoc Loc = 534 Context.getSourceManager().getPresumedLoc(Method->getLocation()); 535 AvailabilityInfo Availability = getAvailability(Method); 536 DocComment Comment; 537 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Method)) 538 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 539 Context.getDiagnostics()); 540 541 // Build declaration fragments, sub-heading, and signature for the method. 542 DeclarationFragments Declaration = 543 DeclarationFragmentsBuilder::getFragmentsForObjCMethod(Method); 544 DeclarationFragments SubHeading = 545 DeclarationFragmentsBuilder::getSubHeading(Method); 546 FunctionSignature Signature = 547 DeclarationFragmentsBuilder::getFunctionSignature(Method); 548 549 API.addObjCMethod(Container, Name, USR, Loc, Availability, Comment, 550 Declaration, SubHeading, Signature, 551 Method->isInstanceMethod()); 552 } 553 } 554 555 void recordObjCProperties(ObjCContainerRecord *Container, 556 const ObjCContainerDecl::prop_range Properties) { 557 for (const auto *Property : Properties) { 558 StringRef Name = Property->getName(); 559 StringRef USR = API.recordUSR(Property); 560 PresumedLoc Loc = 561 Context.getSourceManager().getPresumedLoc(Property->getLocation()); 562 AvailabilityInfo Availability = getAvailability(Property); 563 DocComment Comment; 564 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Property)) 565 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 566 Context.getDiagnostics()); 567 568 // Build declaration fragments and sub-heading for the property. 569 DeclarationFragments Declaration = 570 DeclarationFragmentsBuilder::getFragmentsForObjCProperty(Property); 571 DeclarationFragments SubHeading = 572 DeclarationFragmentsBuilder::getSubHeading(Property); 573 574 StringRef GetterName = 575 API.copyString(Property->getGetterName().getAsString()); 576 StringRef SetterName = 577 API.copyString(Property->getSetterName().getAsString()); 578 579 // Get the attributes for property. 580 unsigned Attributes = ObjCPropertyRecord::NoAttr; 581 if (Property->getPropertyAttributes() & 582 ObjCPropertyAttribute::kind_readonly) 583 Attributes |= ObjCPropertyRecord::ReadOnly; 584 if (Property->getPropertyAttributes() & ObjCPropertyAttribute::kind_class) 585 Attributes |= ObjCPropertyRecord::Class; 586 587 API.addObjCProperty( 588 Container, Name, USR, Loc, Availability, Comment, Declaration, 589 SubHeading, 590 static_cast<ObjCPropertyRecord::AttributeKind>(Attributes), 591 GetterName, SetterName, Property->isOptional()); 592 } 593 } 594 595 void recordObjCInstanceVariables( 596 ObjCContainerRecord *Container, 597 const llvm::iterator_range< 598 DeclContext::specific_decl_iterator<ObjCIvarDecl>> 599 Ivars) { 600 for (const auto *Ivar : Ivars) { 601 StringRef Name = Ivar->getName(); 602 StringRef USR = API.recordUSR(Ivar); 603 PresumedLoc Loc = 604 Context.getSourceManager().getPresumedLoc(Ivar->getLocation()); 605 AvailabilityInfo Availability = getAvailability(Ivar); 606 DocComment Comment; 607 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Ivar)) 608 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 609 Context.getDiagnostics()); 610 611 // Build declaration fragments and sub-heading for the instance variable. 612 DeclarationFragments Declaration = 613 DeclarationFragmentsBuilder::getFragmentsForField(Ivar); 614 DeclarationFragments SubHeading = 615 DeclarationFragmentsBuilder::getSubHeading(Ivar); 616 617 ObjCInstanceVariableRecord::AccessControl Access = 618 Ivar->getCanonicalAccessControl(); 619 620 API.addObjCInstanceVariable(Container, Name, USR, Loc, Availability, 621 Comment, Declaration, SubHeading, Access); 622 } 623 } 624 625 void recordObjCProtocols(ObjCContainerRecord *Container, 626 ObjCInterfaceDecl::protocol_range Protocols) { 627 for (const auto *Protocol : Protocols) 628 Container->Protocols.emplace_back(Protocol->getName(), 629 API.recordUSR(Protocol)); 630 } 631 632 ASTContext &Context; 633 APISet &API; 634 LocationFileChecker &LCF; 635 }; 636 637 class ExtractAPIConsumer : public ASTConsumer { 638 public: 639 ExtractAPIConsumer(ASTContext &Context, 640 std::unique_ptr<LocationFileChecker> LCF, APISet &API) 641 : Visitor(Context, *LCF, API), LCF(std::move(LCF)) {} 642 643 void HandleTranslationUnit(ASTContext &Context) override { 644 // Use ExtractAPIVisitor to traverse symbol declarations in the context. 645 Visitor.TraverseDecl(Context.getTranslationUnitDecl()); 646 } 647 648 private: 649 ExtractAPIVisitor Visitor; 650 std::unique_ptr<LocationFileChecker> LCF; 651 }; 652 653 class MacroCallback : public PPCallbacks { 654 public: 655 MacroCallback(const SourceManager &SM, LocationFileChecker &LCF, APISet &API, 656 Preprocessor &PP) 657 : SM(SM), LCF(LCF), API(API), PP(PP) {} 658 659 void MacroDefined(const Token &MacroNameToken, 660 const MacroDirective *MD) override { 661 auto *MacroInfo = MD->getMacroInfo(); 662 663 if (MacroInfo->isBuiltinMacro()) 664 return; 665 666 auto SourceLoc = MacroNameToken.getLocation(); 667 if (SM.isWrittenInBuiltinFile(SourceLoc) || 668 SM.isWrittenInCommandLineFile(SourceLoc)) 669 return; 670 671 PendingMacros.emplace_back(MacroNameToken, MD); 672 } 673 674 // If a macro gets undefined at some point during preprocessing of the inputs 675 // it means that it isn't an exposed API and we should therefore not add a 676 // macro definition for it. 677 void MacroUndefined(const Token &MacroNameToken, const MacroDefinition &MD, 678 const MacroDirective *Undef) override { 679 // If this macro wasn't previously defined we don't need to do anything 680 // here. 681 if (!Undef) 682 return; 683 684 llvm::erase_if(PendingMacros, [&MD, this](const PendingMacro &PM) { 685 return MD.getMacroInfo()->isIdenticalTo(*PM.MD->getMacroInfo(), PP, 686 /*Syntactically*/ false); 687 }); 688 } 689 690 void EndOfMainFile() override { 691 for (auto &PM : PendingMacros) { 692 // `isUsedForHeaderGuard` is only set when the preprocessor leaves the 693 // file so check for it here. 694 if (PM.MD->getMacroInfo()->isUsedForHeaderGuard()) 695 continue; 696 697 if (!LCF.isLocationInKnownFile(PM.MacroNameToken.getLocation())) 698 continue; 699 700 StringRef Name = PM.MacroNameToken.getIdentifierInfo()->getName(); 701 PresumedLoc Loc = SM.getPresumedLoc(PM.MacroNameToken.getLocation()); 702 StringRef USR = 703 API.recordUSRForMacro(Name, PM.MacroNameToken.getLocation(), SM); 704 705 API.addMacroDefinition( 706 Name, USR, Loc, 707 DeclarationFragmentsBuilder::getFragmentsForMacro(Name, PM.MD), 708 DeclarationFragmentsBuilder::getSubHeadingForMacro(Name)); 709 } 710 711 PendingMacros.clear(); 712 } 713 714 private: 715 struct PendingMacro { 716 Token MacroNameToken; 717 const MacroDirective *MD; 718 719 PendingMacro(const Token &MacroNameToken, const MacroDirective *MD) 720 : MacroNameToken(MacroNameToken), MD(MD) {} 721 }; 722 723 const SourceManager &SM; 724 LocationFileChecker &LCF; 725 APISet &API; 726 Preprocessor &PP; 727 llvm::SmallVector<PendingMacro> PendingMacros; 728 }; 729 730 } // namespace 731 732 std::unique_ptr<ASTConsumer> 733 ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { 734 OS = CreateOutputFile(CI, InFile); 735 if (!OS) 736 return nullptr; 737 738 ProductName = CI.getFrontendOpts().ProductName; 739 740 // Now that we have enough information about the language options and the 741 // target triple, let's create the APISet before anyone uses it. 742 API = std::make_unique<APISet>( 743 CI.getTarget().getTriple(), 744 CI.getFrontendOpts().Inputs.back().getKind().getLanguage()); 745 746 auto LCF = std::make_unique<LocationFileChecker>(CI.getSourceManager(), 747 KnownInputFiles); 748 749 CI.getPreprocessor().addPPCallbacks(std::make_unique<MacroCallback>( 750 CI.getSourceManager(), *LCF, *API, CI.getPreprocessor())); 751 752 return std::make_unique<ExtractAPIConsumer>(CI.getASTContext(), 753 std::move(LCF), *API); 754 } 755 756 bool ExtractAPIAction::PrepareToExecuteAction(CompilerInstance &CI) { 757 auto &Inputs = CI.getFrontendOpts().Inputs; 758 if (Inputs.empty()) 759 return true; 760 761 auto Kind = Inputs[0].getKind(); 762 763 // Convert the header file inputs into a single input buffer. 764 SmallString<256> HeaderContents; 765 for (const FrontendInputFile &FIF : Inputs) { 766 if (Kind.isObjectiveC()) 767 HeaderContents += "#import"; 768 else 769 HeaderContents += "#include"; 770 HeaderContents += " \""; 771 HeaderContents += FIF.getFile(); 772 HeaderContents += "\"\n"; 773 774 KnownInputFiles.emplace_back(FIF.getFile()); 775 } 776 777 Buffer = llvm::MemoryBuffer::getMemBufferCopy(HeaderContents, 778 getInputBufferName()); 779 780 // Set that buffer up as our "real" input in the CompilerInstance. 781 Inputs.clear(); 782 Inputs.emplace_back(Buffer->getMemBufferRef(), Kind, /*IsSystem*/ false); 783 784 return true; 785 } 786 787 void ExtractAPIAction::EndSourceFileAction() { 788 if (!OS) 789 return; 790 791 // Setup a SymbolGraphSerializer to write out collected API information in 792 // the Symbol Graph format. 793 // FIXME: Make the kind of APISerializer configurable. 794 SymbolGraphSerializer SGSerializer(*API, ProductName); 795 SGSerializer.serialize(*OS); 796 OS->flush(); 797 } 798 799 std::unique_ptr<raw_pwrite_stream> 800 ExtractAPIAction::CreateOutputFile(CompilerInstance &CI, StringRef InFile) { 801 std::unique_ptr<raw_pwrite_stream> OS = 802 CI.createDefaultOutputFile(/*Binary=*/false, InFile, /*Extension=*/"json", 803 /*RemoveFileOnSignal=*/false); 804 if (!OS) 805 return nullptr; 806 return OS; 807 } 808