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 private: 264 /// Get availability information of the declaration \p D. 265 AvailabilityInfo getAvailability(const Decl *D) const { 266 StringRef PlatformName = Context.getTargetInfo().getPlatformName(); 267 268 AvailabilityInfo Availability; 269 // Collect availability attributes from all redeclarations. 270 for (const auto *RD : D->redecls()) { 271 for (const auto *A : RD->specific_attrs<AvailabilityAttr>()) { 272 if (A->getPlatform()->getName() != PlatformName) 273 continue; 274 Availability = AvailabilityInfo(A->getIntroduced(), A->getDeprecated(), 275 A->getObsoleted(), A->getUnavailable(), 276 /* UnconditionallyDeprecated */ false, 277 /* UnconditionallyUnavailable */ false); 278 break; 279 } 280 281 if (const auto *A = RD->getAttr<UnavailableAttr>()) 282 if (!A->isImplicit()) { 283 Availability.Unavailable = true; 284 Availability.UnconditionallyUnavailable = true; 285 } 286 287 if (const auto *A = RD->getAttr<DeprecatedAttr>()) 288 if (!A->isImplicit()) 289 Availability.UnconditionallyDeprecated = true; 290 } 291 292 return Availability; 293 } 294 295 /// Collect API information for the enum constants and associate with the 296 /// parent enum. 297 void recordEnumConstants(EnumRecord *EnumRecord, 298 const EnumDecl::enumerator_range Constants) { 299 for (const auto *Constant : Constants) { 300 // Collect symbol information. 301 StringRef Name = Constant->getName(); 302 StringRef USR = API.recordUSR(Constant); 303 PresumedLoc Loc = 304 Context.getSourceManager().getPresumedLoc(Constant->getLocation()); 305 AvailabilityInfo Availability = getAvailability(Constant); 306 DocComment Comment; 307 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Constant)) 308 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 309 Context.getDiagnostics()); 310 311 // Build declaration fragments and sub-heading for the enum constant. 312 DeclarationFragments Declaration = 313 DeclarationFragmentsBuilder::getFragmentsForEnumConstant(Constant); 314 DeclarationFragments SubHeading = 315 DeclarationFragmentsBuilder::getSubHeading(Constant); 316 317 API.addEnumConstant(EnumRecord, Name, USR, Loc, Availability, Comment, 318 Declaration, SubHeading); 319 } 320 } 321 322 /// Collect API information for the struct fields and associate with the 323 /// parent struct. 324 void recordStructFields(StructRecord *StructRecord, 325 const RecordDecl::field_range Fields) { 326 for (const auto *Field : Fields) { 327 // Collect symbol information. 328 StringRef Name = Field->getName(); 329 StringRef USR = API.recordUSR(Field); 330 PresumedLoc Loc = 331 Context.getSourceManager().getPresumedLoc(Field->getLocation()); 332 AvailabilityInfo Availability = getAvailability(Field); 333 DocComment Comment; 334 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Field)) 335 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 336 Context.getDiagnostics()); 337 338 // Build declaration fragments and sub-heading for the struct field. 339 DeclarationFragments Declaration = 340 DeclarationFragmentsBuilder::getFragmentsForField(Field); 341 DeclarationFragments SubHeading = 342 DeclarationFragmentsBuilder::getSubHeading(Field); 343 344 API.addStructField(StructRecord, Name, USR, Loc, Availability, Comment, 345 Declaration, SubHeading); 346 } 347 } 348 349 /// Collect API information for the Objective-C methods and associate with the 350 /// parent container. 351 void recordObjCMethods(ObjCContainerRecord *Container, 352 const ObjCContainerDecl::method_range Methods) { 353 for (const auto *Method : Methods) { 354 // Don't record selectors for properties. 355 if (Method->isPropertyAccessor()) 356 continue; 357 358 StringRef Name = API.copyString(Method->getSelector().getAsString()); 359 StringRef USR = API.recordUSR(Method); 360 PresumedLoc Loc = 361 Context.getSourceManager().getPresumedLoc(Method->getLocation()); 362 AvailabilityInfo Availability = getAvailability(Method); 363 DocComment Comment; 364 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Method)) 365 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 366 Context.getDiagnostics()); 367 368 // Build declaration fragments, sub-heading, and signature for the method. 369 DeclarationFragments Declaration = 370 DeclarationFragmentsBuilder::getFragmentsForObjCMethod(Method); 371 DeclarationFragments SubHeading = 372 DeclarationFragmentsBuilder::getSubHeading(Method); 373 FunctionSignature Signature = 374 DeclarationFragmentsBuilder::getFunctionSignature(Method); 375 376 API.addObjCMethod(Container, Name, USR, Loc, Availability, Comment, 377 Declaration, SubHeading, Signature, 378 Method->isInstanceMethod()); 379 } 380 } 381 382 void recordObjCProperties(ObjCContainerRecord *Container, 383 const ObjCContainerDecl::prop_range Properties) { 384 for (const auto *Property : Properties) { 385 StringRef Name = Property->getName(); 386 StringRef USR = API.recordUSR(Property); 387 PresumedLoc Loc = 388 Context.getSourceManager().getPresumedLoc(Property->getLocation()); 389 AvailabilityInfo Availability = getAvailability(Property); 390 DocComment Comment; 391 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Property)) 392 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 393 Context.getDiagnostics()); 394 395 // Build declaration fragments and sub-heading for the property. 396 DeclarationFragments Declaration = 397 DeclarationFragmentsBuilder::getFragmentsForObjCProperty(Property); 398 DeclarationFragments SubHeading = 399 DeclarationFragmentsBuilder::getSubHeading(Property); 400 401 StringRef GetterName = 402 API.copyString(Property->getGetterName().getAsString()); 403 StringRef SetterName = 404 API.copyString(Property->getSetterName().getAsString()); 405 406 // Get the attributes for property. 407 unsigned Attributes = ObjCPropertyRecord::NoAttr; 408 if (Property->getPropertyAttributes() & 409 ObjCPropertyAttribute::kind_readonly) 410 Attributes |= ObjCPropertyRecord::ReadOnly; 411 if (Property->getPropertyAttributes() & ObjCPropertyAttribute::kind_class) 412 Attributes |= ObjCPropertyRecord::Class; 413 414 API.addObjCProperty( 415 Container, Name, USR, Loc, Availability, Comment, Declaration, 416 SubHeading, 417 static_cast<ObjCPropertyRecord::AttributeKind>(Attributes), 418 GetterName, SetterName, Property->isOptional()); 419 } 420 } 421 422 void recordObjCInstanceVariables( 423 ObjCContainerRecord *Container, 424 const llvm::iterator_range< 425 DeclContext::specific_decl_iterator<ObjCIvarDecl>> 426 Ivars) { 427 for (const auto *Ivar : Ivars) { 428 StringRef Name = Ivar->getName(); 429 StringRef USR = API.recordUSR(Ivar); 430 PresumedLoc Loc = 431 Context.getSourceManager().getPresumedLoc(Ivar->getLocation()); 432 AvailabilityInfo Availability = getAvailability(Ivar); 433 DocComment Comment; 434 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Ivar)) 435 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 436 Context.getDiagnostics()); 437 438 // Build declaration fragments and sub-heading for the instance variable. 439 DeclarationFragments Declaration = 440 DeclarationFragmentsBuilder::getFragmentsForField(Ivar); 441 DeclarationFragments SubHeading = 442 DeclarationFragmentsBuilder::getSubHeading(Ivar); 443 444 ObjCInstanceVariableRecord::AccessControl Access = 445 Ivar->getCanonicalAccessControl(); 446 447 API.addObjCInstanceVariable(Container, Name, USR, Loc, Availability, 448 Comment, Declaration, SubHeading, Access); 449 } 450 } 451 452 void recordObjCProtocols(ObjCContainerRecord *Container, 453 ObjCInterfaceDecl::protocol_range Protocols) { 454 for (const auto *Protocol : Protocols) 455 Container->Protocols.emplace_back(Protocol->getName(), 456 API.recordUSR(Protocol)); 457 } 458 459 ASTContext &Context; 460 APISet API; 461 }; 462 463 class ExtractAPIConsumer : public ASTConsumer { 464 public: 465 ExtractAPIConsumer(ASTContext &Context, StringRef ProductName, Language Lang, 466 std::unique_ptr<raw_pwrite_stream> OS) 467 : Visitor(Context, Lang), ProductName(ProductName), OS(std::move(OS)) {} 468 469 void HandleTranslationUnit(ASTContext &Context) override { 470 // Use ExtractAPIVisitor to traverse symbol declarations in the context. 471 Visitor.TraverseDecl(Context.getTranslationUnitDecl()); 472 473 // Setup a SymbolGraphSerializer to write out collected API information in 474 // the Symbol Graph format. 475 // FIXME: Make the kind of APISerializer configurable. 476 SymbolGraphSerializer SGSerializer(Visitor.getAPI(), ProductName); 477 SGSerializer.serialize(*OS); 478 } 479 480 private: 481 ExtractAPIVisitor Visitor; 482 std::string ProductName; 483 std::unique_ptr<raw_pwrite_stream> OS; 484 }; 485 486 } // namespace 487 488 std::unique_ptr<ASTConsumer> 489 ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { 490 std::unique_ptr<raw_pwrite_stream> OS = CreateOutputFile(CI, InFile); 491 if (!OS) 492 return nullptr; 493 return std::make_unique<ExtractAPIConsumer>( 494 CI.getASTContext(), CI.getInvocation().getFrontendOpts().ProductName, 495 CI.getFrontendOpts().Inputs.back().getKind().getLanguage(), 496 std::move(OS)); 497 } 498 499 bool ExtractAPIAction::PrepareToExecuteAction(CompilerInstance &CI) { 500 auto &Inputs = CI.getFrontendOpts().Inputs; 501 if (Inputs.empty()) 502 return true; 503 504 auto Kind = Inputs[0].getKind(); 505 506 // Convert the header file inputs into a single input buffer. 507 SmallString<256> HeaderContents; 508 for (const FrontendInputFile &FIF : Inputs) { 509 if (Kind.isObjectiveC()) 510 HeaderContents += "#import"; 511 else 512 HeaderContents += "#include"; 513 HeaderContents += " \""; 514 HeaderContents += FIF.getFile(); 515 HeaderContents += "\"\n"; 516 } 517 518 Buffer = llvm::MemoryBuffer::getMemBufferCopy(HeaderContents, 519 getInputBufferName()); 520 521 // Set that buffer up as our "real" input in the CompilerInstance. 522 Inputs.clear(); 523 Inputs.emplace_back(Buffer->getMemBufferRef(), Kind, /*IsSystem*/ false); 524 525 return true; 526 } 527 528 std::unique_ptr<raw_pwrite_stream> 529 ExtractAPIAction::CreateOutputFile(CompilerInstance &CI, StringRef InFile) { 530 std::unique_ptr<raw_pwrite_stream> OS = 531 CI.createDefaultOutputFile(/*Binary=*/false, InFile, /*Extension=*/"json", 532 /*RemoveFileOnSignal=*/false); 533 if (!OS) 534 return nullptr; 535 return OS; 536 } 537