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 bool VisitEnumDecl(const EnumDecl *Decl) { 149 if (!Decl->isComplete()) 150 return true; 151 152 // Skip forward declaration. 153 if (!Decl->isThisDeclarationADefinition()) 154 return true; 155 156 // Collect symbol information. 157 StringRef Name = Decl->getName(); 158 StringRef USR = API.recordUSR(Decl); 159 PresumedLoc Loc = 160 Context.getSourceManager().getPresumedLoc(Decl->getLocation()); 161 AvailabilityInfo Availability = getAvailability(Decl); 162 DocComment Comment; 163 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) 164 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 165 Context.getDiagnostics()); 166 167 // Build declaration fragments and sub-heading for the enum. 168 DeclarationFragments Declaration = 169 DeclarationFragmentsBuilder::getFragmentsForEnum(Decl); 170 DeclarationFragments SubHeading = 171 DeclarationFragmentsBuilder::getSubHeading(Decl); 172 173 EnumRecord *EnumRecord = API.addEnum(Name, USR, Loc, Availability, Comment, 174 Declaration, SubHeading); 175 176 // Now collect information about the enumerators in this enum. 177 recordEnumConstants(EnumRecord, Decl->enumerators()); 178 179 return true; 180 } 181 182 private: 183 /// Get availability information of the declaration \p D. 184 AvailabilityInfo getAvailability(const Decl *D) const { 185 StringRef PlatformName = Context.getTargetInfo().getPlatformName(); 186 187 AvailabilityInfo Availability; 188 // Collect availability attributes from all redeclarations. 189 for (const auto *RD : D->redecls()) { 190 for (const auto *A : RD->specific_attrs<AvailabilityAttr>()) { 191 if (A->getPlatform()->getName() != PlatformName) 192 continue; 193 Availability = AvailabilityInfo(A->getIntroduced(), A->getDeprecated(), 194 A->getObsoleted(), A->getUnavailable(), 195 /* UnconditionallyDeprecated */ false, 196 /* UnconditionallyUnavailable */ false); 197 break; 198 } 199 200 if (const auto *A = RD->getAttr<UnavailableAttr>()) 201 if (!A->isImplicit()) { 202 Availability.Unavailable = true; 203 Availability.UnconditionallyUnavailable = true; 204 } 205 206 if (const auto *A = RD->getAttr<DeprecatedAttr>()) 207 if (!A->isImplicit()) 208 Availability.UnconditionallyDeprecated = true; 209 } 210 211 return Availability; 212 } 213 214 /// Collect API information for the enum constants and associate with the 215 /// parent enum. 216 void recordEnumConstants(EnumRecord *EnumRecord, 217 const EnumDecl::enumerator_range Constants) { 218 for (const auto *Constant : Constants) { 219 // Collect symbol information. 220 StringRef Name = Constant->getName(); 221 StringRef USR = API.recordUSR(Constant); 222 PresumedLoc Loc = 223 Context.getSourceManager().getPresumedLoc(Constant->getLocation()); 224 AvailabilityInfo Availability = getAvailability(Constant); 225 DocComment Comment; 226 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Constant)) 227 Comment = RawComment->getFormattedLines(Context.getSourceManager(), 228 Context.getDiagnostics()); 229 230 // Build declaration fragments and sub-heading for the enum constant. 231 DeclarationFragments Declaration = 232 DeclarationFragmentsBuilder::getFragmentsForEnumConstant(Constant); 233 DeclarationFragments SubHeading = 234 DeclarationFragmentsBuilder::getSubHeading(Constant); 235 236 API.addEnumConstant(EnumRecord, Name, USR, Loc, Availability, Comment, 237 Declaration, SubHeading); 238 } 239 } 240 241 ASTContext &Context; 242 APISet API; 243 }; 244 245 class ExtractAPIConsumer : public ASTConsumer { 246 public: 247 ExtractAPIConsumer(ASTContext &Context, StringRef ProductName, 248 std::unique_ptr<raw_pwrite_stream> OS) 249 : Visitor(Context), ProductName(ProductName), OS(std::move(OS)) {} 250 251 void HandleTranslationUnit(ASTContext &Context) override { 252 // Use ExtractAPIVisitor to traverse symbol declarations in the context. 253 Visitor.TraverseDecl(Context.getTranslationUnitDecl()); 254 255 // Setup a SymbolGraphSerializer to write out collected API information in 256 // the Symbol Graph format. 257 // FIXME: Make the kind of APISerializer configurable. 258 SymbolGraphSerializer SGSerializer(Visitor.getAPI(), ProductName); 259 SGSerializer.serialize(*OS); 260 } 261 262 private: 263 ExtractAPIVisitor Visitor; 264 std::string ProductName; 265 std::unique_ptr<raw_pwrite_stream> OS; 266 }; 267 268 } // namespace 269 270 std::unique_ptr<ASTConsumer> 271 ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { 272 std::unique_ptr<raw_pwrite_stream> OS = CreateOutputFile(CI, InFile); 273 if (!OS) 274 return nullptr; 275 return std::make_unique<ExtractAPIConsumer>( 276 CI.getASTContext(), CI.getInvocation().getFrontendOpts().ProductName, 277 std::move(OS)); 278 } 279 280 std::unique_ptr<raw_pwrite_stream> 281 ExtractAPIAction::CreateOutputFile(CompilerInstance &CI, StringRef InFile) { 282 std::unique_ptr<raw_pwrite_stream> OS = 283 CI.createDefaultOutputFile(/*Binary=*/false, InFile, /*Extension=*/"json", 284 /*RemoveFileOnSignal=*/false); 285 if (!OS) 286 return nullptr; 287 return OS; 288 } 289