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