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 Preprocessor &PP) 654 : SM(SM), LCF(LCF), API(API), PP(PP) {} 655 656 void MacroDefined(const Token &MacroNameToken, 657 const MacroDirective *MD) override { 658 auto *MacroInfo = MD->getMacroInfo(); 659 660 if (MacroInfo->isBuiltinMacro()) 661 return; 662 663 auto SourceLoc = MacroNameToken.getLocation(); 664 if (SM.isWrittenInBuiltinFile(SourceLoc) || 665 SM.isWrittenInCommandLineFile(SourceLoc)) 666 return; 667 668 PendingMacros.emplace_back(MacroNameToken, MD); 669 } 670 671 // If a macro gets undefined at some point during preprocessing of the inputs 672 // it means that it isn't an exposed API and we should therefore not add a 673 // macro definition for it. 674 void MacroUndefined(const Token &MacroNameToken, const MacroDefinition &MD, 675 const MacroDirective *Undef) override { 676 // If this macro wasn't previously defined we don't need to do anything 677 // here. 678 if (!Undef) 679 return; 680 681 llvm::erase_if(PendingMacros, [&MD, this](const PendingMacro &PM) { 682 return MD.getMacroInfo()->isIdenticalTo(*PM.MD->getMacroInfo(), PP, 683 /*Syntactically*/ false); 684 }); 685 } 686 687 void EndOfMainFile() override { 688 for (auto &PM : PendingMacros) { 689 // `isUsedForHeaderGuard` is only set when the preprocessor leaves the 690 // file so check for it here. 691 if (PM.MD->getMacroInfo()->isUsedForHeaderGuard()) 692 continue; 693 694 if (!LCF.isLocationInKnownFile(PM.MacroNameToken.getLocation())) 695 continue; 696 697 StringRef Name = PM.MacroNameToken.getIdentifierInfo()->getName(); 698 PresumedLoc Loc = SM.getPresumedLoc(PM.MacroNameToken.getLocation()); 699 StringRef USR = 700 API.recordUSRForMacro(Name, PM.MacroNameToken.getLocation(), SM); 701 702 API.addMacroDefinition( 703 Name, USR, Loc, 704 DeclarationFragmentsBuilder::getFragmentsForMacro(Name, PM.MD), 705 DeclarationFragmentsBuilder::getSubHeadingForMacro(Name)); 706 } 707 708 PendingMacros.clear(); 709 } 710 711 private: 712 struct PendingMacro { 713 Token MacroNameToken; 714 const MacroDirective *MD; 715 716 PendingMacro(const Token &MacroNameToken, const MacroDirective *MD) 717 : MacroNameToken(MacroNameToken), MD(MD) {} 718 }; 719 720 const SourceManager &SM; 721 LocationFileChecker &LCF; 722 APISet &API; 723 Preprocessor &PP; 724 llvm::SmallVector<PendingMacro> PendingMacros; 725 }; 726 727 } // namespace 728 729 std::unique_ptr<ASTConsumer> 730 ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { 731 OS = CreateOutputFile(CI, InFile); 732 if (!OS) 733 return nullptr; 734 735 ProductName = CI.getFrontendOpts().ProductName; 736 737 // Now that we have enough information about the language options and the 738 // target triple, let's create the APISet before anyone uses it. 739 API = std::make_unique<APISet>( 740 CI.getTarget().getTriple(), 741 CI.getFrontendOpts().Inputs.back().getKind().getLanguage()); 742 743 auto LCF = std::make_unique<LocationFileChecker>(CI.getSourceManager(), 744 KnownInputFiles); 745 746 CI.getPreprocessor().addPPCallbacks(std::make_unique<MacroCallback>( 747 CI.getSourceManager(), *LCF, *API, CI.getPreprocessor())); 748 749 return std::make_unique<ExtractAPIConsumer>(CI.getASTContext(), 750 std::move(LCF), *API); 751 } 752 753 bool ExtractAPIAction::PrepareToExecuteAction(CompilerInstance &CI) { 754 auto &Inputs = CI.getFrontendOpts().Inputs; 755 if (Inputs.empty()) 756 return true; 757 758 auto Kind = Inputs[0].getKind(); 759 760 // Convert the header file inputs into a single input buffer. 761 SmallString<256> HeaderContents; 762 for (const FrontendInputFile &FIF : Inputs) { 763 if (Kind.isObjectiveC()) 764 HeaderContents += "#import"; 765 else 766 HeaderContents += "#include"; 767 HeaderContents += " \""; 768 HeaderContents += FIF.getFile(); 769 HeaderContents += "\"\n"; 770 771 KnownInputFiles.emplace_back(FIF.getFile()); 772 } 773 774 Buffer = llvm::MemoryBuffer::getMemBufferCopy(HeaderContents, 775 getInputBufferName()); 776 777 // Set that buffer up as our "real" input in the CompilerInstance. 778 Inputs.clear(); 779 Inputs.emplace_back(Buffer->getMemBufferRef(), Kind, /*IsSystem*/ false); 780 781 return true; 782 } 783 784 void ExtractAPIAction::EndSourceFileAction() { 785 if (!OS) 786 return; 787 788 // Setup a SymbolGraphSerializer to write out collected API information in 789 // the Symbol Graph format. 790 // FIXME: Make the kind of APISerializer configurable. 791 SymbolGraphSerializer SGSerializer(*API, ProductName); 792 SGSerializer.serialize(*OS); 793 OS->flush(); 794 } 795 796 std::unique_ptr<raw_pwrite_stream> 797 ExtractAPIAction::CreateOutputFile(CompilerInstance &CI, StringRef InFile) { 798 std::unique_ptr<raw_pwrite_stream> OS = 799 CI.createDefaultOutputFile(/*Binary=*/false, InFile, /*Extension=*/"json", 800 /*RemoveFileOnSignal=*/false); 801 if (!OS) 802 return nullptr; 803 return OS; 804 } 805