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