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 "llvm/Support/raw_ostream.h" 31 32 using namespace clang; 33 using namespace extractapi; 34 35 namespace { 36 37 /// The RecursiveASTVisitor to traverse symbol declarations and collect API 38 /// information. 39 class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> { 40 public: 41 explicit ExtractAPIVisitor(ASTContext &Context) 42 : Context(Context), 43 API(Context.getTargetInfo().getTriple(), Context.getLangOpts()) {} 44 45 const APISet &getAPI() const { return API; } 46 47 bool VisitVarDecl(const VarDecl *Decl) { 48 // Skip function parameters. 49 if (isa<ParmVarDecl>(Decl)) 50 return true; 51 52 // Skip non-global variables in records (struct/union/class). 53 if (Decl->getDeclContext()->isRecord()) 54 return true; 55 56 // Skip local variables inside function or method. 57 if (!Decl->isDefinedOutsideFunctionOrMethod()) 58 return true; 59 60 // If this is a template but not specialization or instantiation, skip. 61 if (Decl->getASTContext().getTemplateOrSpecializationInfo(Decl) && 62 Decl->getTemplateSpecializationKind() == TSK_Undeclared) 63 return true; 64 65 // Collect symbol information. 66 StringRef Name = Decl->getName(); 67 StringRef USR = API.recordUSR(Decl); 68 PresumedLoc Loc = 69 Context.getSourceManager().getPresumedLoc(Decl->getLocation()); 70 AvailabilityInfo Availability = getAvailability(Decl); 71 LinkageInfo Linkage = Decl->getLinkageAndVisibility(); 72 DocComment Comment; 73 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) 74 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 75 Context.getDiagnostics()); 76 77 // Build declaration fragments and sub-heading for the variable. 78 DeclarationFragments Declaration = 79 DeclarationFragmentsBuilder::getFragmentsForVar(Decl); 80 DeclarationFragments SubHeading = 81 DeclarationFragmentsBuilder::getSubHeading(Decl); 82 83 // Add the global variable record to the API set. 84 API.addGlobalVar(Name, USR, Loc, Availability, Linkage, Comment, 85 Declaration, SubHeading); 86 return true; 87 } 88 89 bool VisitFunctionDecl(const FunctionDecl *Decl) { 90 if (const auto *Method = dyn_cast<CXXMethodDecl>(Decl)) { 91 // Skip member function in class templates. 92 if (Method->getParent()->getDescribedClassTemplate() != nullptr) 93 return true; 94 95 // Skip methods in records. 96 for (auto P : Context.getParents(*Method)) { 97 if (P.get<CXXRecordDecl>()) 98 return true; 99 } 100 101 // Skip ConstructorDecl and DestructorDecl. 102 if (isa<CXXConstructorDecl>(Method) || isa<CXXDestructorDecl>(Method)) 103 return true; 104 } 105 106 // Skip templated functions. 107 switch (Decl->getTemplatedKind()) { 108 case FunctionDecl::TK_NonTemplate: 109 break; 110 case FunctionDecl::TK_MemberSpecialization: 111 case FunctionDecl::TK_FunctionTemplateSpecialization: 112 if (auto *TemplateInfo = Decl->getTemplateSpecializationInfo()) { 113 if (!TemplateInfo->isExplicitInstantiationOrSpecialization()) 114 return true; 115 } 116 break; 117 case FunctionDecl::TK_FunctionTemplate: 118 case FunctionDecl::TK_DependentFunctionTemplateSpecialization: 119 return true; 120 } 121 122 // Collect symbol information. 123 StringRef Name = Decl->getName(); 124 StringRef USR = API.recordUSR(Decl); 125 PresumedLoc Loc = 126 Context.getSourceManager().getPresumedLoc(Decl->getLocation()); 127 AvailabilityInfo Availability = getAvailability(Decl); 128 LinkageInfo Linkage = Decl->getLinkageAndVisibility(); 129 DocComment Comment; 130 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) 131 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 132 Context.getDiagnostics()); 133 134 // Build declaration fragments, sub-heading, and signature of the function. 135 DeclarationFragments Declaration = 136 DeclarationFragmentsBuilder::getFragmentsForFunction(Decl); 137 DeclarationFragments SubHeading = 138 DeclarationFragmentsBuilder::getSubHeading(Decl); 139 FunctionSignature Signature = 140 DeclarationFragmentsBuilder::getFunctionSignature(Decl); 141 142 // Add the function record to the API set. 143 API.addFunction(Name, USR, Loc, Availability, Linkage, Comment, Declaration, 144 SubHeading, Signature); 145 return true; 146 } 147 148 private: 149 /// Get availability information of the declaration \p D. 150 AvailabilityInfo getAvailability(const Decl *D) const { 151 StringRef PlatformName = Context.getTargetInfo().getPlatformName(); 152 153 AvailabilityInfo Availability; 154 // Collect availability attributes from all redeclarations. 155 for (const auto *RD : D->redecls()) { 156 for (const auto *A : RD->specific_attrs<AvailabilityAttr>()) { 157 if (A->getPlatform()->getName() != PlatformName) 158 continue; 159 Availability = AvailabilityInfo(A->getIntroduced(), A->getDeprecated(), 160 A->getObsoleted(), A->getUnavailable(), 161 /* UnconditionallyDeprecated */ false, 162 /* UnconditionallyUnavailable */ false); 163 break; 164 } 165 166 if (const auto *A = RD->getAttr<UnavailableAttr>()) 167 if (!A->isImplicit()) { 168 Availability.Unavailable = true; 169 Availability.UnconditionallyUnavailable = true; 170 } 171 172 if (const auto *A = RD->getAttr<DeprecatedAttr>()) 173 if (!A->isImplicit()) 174 Availability.UnconditionallyDeprecated = true; 175 } 176 177 return Availability; 178 } 179 180 ASTContext &Context; 181 APISet API; 182 }; 183 184 class ExtractAPIConsumer : public ASTConsumer { 185 public: 186 ExtractAPIConsumer(ASTContext &Context, std::unique_ptr<raw_pwrite_stream> OS) 187 : Visitor(Context), OS(std::move(OS)) {} 188 189 void HandleTranslationUnit(ASTContext &Context) override { 190 // Use ExtractAPIVisitor to traverse symbol declarations in the context. 191 Visitor.TraverseDecl(Context.getTranslationUnitDecl()); 192 193 // Setup a SymbolGraphSerializer to write out collected API information in 194 // the Symbol Graph format. 195 // FIXME: Make the kind of APISerializer configurable. 196 SymbolGraphSerializer SGSerializer(Visitor.getAPI()); 197 SGSerializer.serialize(*OS); 198 } 199 200 private: 201 ExtractAPIVisitor Visitor; 202 std::unique_ptr<raw_pwrite_stream> OS; 203 }; 204 205 } // namespace 206 207 std::unique_ptr<ASTConsumer> 208 ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { 209 std::unique_ptr<raw_pwrite_stream> OS = CreateOutputFile(CI, InFile); 210 if (!OS) 211 return nullptr; 212 return std::make_unique<ExtractAPIConsumer>(CI.getASTContext(), 213 std::move(OS)); 214 } 215 216 std::unique_ptr<raw_pwrite_stream> 217 ExtractAPIAction::CreateOutputFile(CompilerInstance &CI, StringRef InFile) { 218 std::unique_ptr<raw_pwrite_stream> OS = 219 CI.createDefaultOutputFile(/*Binary=*/false, InFile, /*Extension=*/"json", 220 /*RemoveFileOnSignal=*/false); 221 if (!OS) 222 return nullptr; 223 return OS; 224 } 225