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