//===- ExtractAPI/ExtractAPIConsumer.cpp ------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// /// \file /// This file implements the ExtractAPIAction, and ASTVisitor/Consumer to /// collect API information. /// //===----------------------------------------------------------------------===// #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/ParentMapContext.h" #include "clang/AST/RawCommentList.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/Basic/TargetInfo.h" #include "clang/ExtractAPI/API.h" #include "clang/ExtractAPI/AvailabilityInfo.h" #include "clang/ExtractAPI/DeclarationFragments.h" #include "clang/ExtractAPI/FrontendActions.h" #include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h" #include "clang/Frontend/ASTConsumers.h" #include "clang/Frontend/CompilerInstance.h" #include "llvm/Support/raw_ostream.h" using namespace clang; using namespace extractapi; namespace { /// The RecursiveASTVisitor to traverse symbol declarations and collect API /// information. class ExtractAPIVisitor : public RecursiveASTVisitor { public: explicit ExtractAPIVisitor(ASTContext &Context) : Context(Context), API(Context.getTargetInfo().getTriple(), Context.getLangOpts()) {} const APISet &getAPI() const { return API; } bool VisitVarDecl(const VarDecl *Decl) { // Skip function parameters. if (isa(Decl)) return true; // Skip non-global variables in records (struct/union/class). if (Decl->getDeclContext()->isRecord()) return true; // Skip local variables inside function or method. if (!Decl->isDefinedOutsideFunctionOrMethod()) return true; // If this is a template but not specialization or instantiation, skip. if (Decl->getASTContext().getTemplateOrSpecializationInfo(Decl) && Decl->getTemplateSpecializationKind() == TSK_Undeclared) return true; // Collect symbol information. StringRef Name = Decl->getName(); StringRef USR = API.recordUSR(Decl); PresumedLoc Loc = Context.getSourceManager().getPresumedLoc(Decl->getLocation()); AvailabilityInfo Availability = getAvailability(Decl); LinkageInfo Linkage = Decl->getLinkageAndVisibility(); DocComment Comment; if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) Comment = RawComment->getFormattedLines(Context.getSourceManager(), Context.getDiagnostics()); // Build declaration fragments and sub-heading for the variable. DeclarationFragments Declaration = DeclarationFragmentsBuilder::getFragmentsForVar(Decl); DeclarationFragments SubHeading = DeclarationFragmentsBuilder::getSubHeading(Decl); // Add the global variable record to the API set. API.addGlobalVar(Name, USR, Loc, Availability, Linkage, Comment, Declaration, SubHeading); return true; } bool VisitFunctionDecl(const FunctionDecl *Decl) { if (const auto *Method = dyn_cast(Decl)) { // Skip member function in class templates. if (Method->getParent()->getDescribedClassTemplate() != nullptr) return true; // Skip methods in records. for (auto P : Context.getParents(*Method)) { if (P.get()) return true; } // Skip ConstructorDecl and DestructorDecl. if (isa(Method) || isa(Method)) return true; } // Skip templated functions. switch (Decl->getTemplatedKind()) { case FunctionDecl::TK_NonTemplate: break; case FunctionDecl::TK_MemberSpecialization: case FunctionDecl::TK_FunctionTemplateSpecialization: if (auto *TemplateInfo = Decl->getTemplateSpecializationInfo()) { if (!TemplateInfo->isExplicitInstantiationOrSpecialization()) return true; } break; case FunctionDecl::TK_FunctionTemplate: case FunctionDecl::TK_DependentFunctionTemplateSpecialization: return true; } // Collect symbol information. StringRef Name = Decl->getName(); StringRef USR = API.recordUSR(Decl); PresumedLoc Loc = Context.getSourceManager().getPresumedLoc(Decl->getLocation()); AvailabilityInfo Availability = getAvailability(Decl); LinkageInfo Linkage = Decl->getLinkageAndVisibility(); DocComment Comment; if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) Comment = RawComment->getFormattedLines(Context.getSourceManager(), Context.getDiagnostics()); // Build declaration fragments, sub-heading, and signature of the function. DeclarationFragments Declaration = DeclarationFragmentsBuilder::getFragmentsForFunction(Decl); DeclarationFragments SubHeading = DeclarationFragmentsBuilder::getSubHeading(Decl); FunctionSignature Signature = DeclarationFragmentsBuilder::getFunctionSignature(Decl); // Add the function record to the API set. API.addFunction(Name, USR, Loc, Availability, Linkage, Comment, Declaration, SubHeading, Signature); return true; } private: /// Get availability information of the declaration \p D. AvailabilityInfo getAvailability(const Decl *D) const { StringRef PlatformName = Context.getTargetInfo().getPlatformName(); AvailabilityInfo Availability; // Collect availability attributes from all redeclarations. for (const auto *RD : D->redecls()) { for (const auto *A : RD->specific_attrs()) { if (A->getPlatform()->getName() != PlatformName) continue; Availability = AvailabilityInfo(A->getIntroduced(), A->getDeprecated(), A->getObsoleted(), A->getUnavailable(), /* UnconditionallyDeprecated */ false, /* UnconditionallyUnavailable */ false); break; } if (const auto *A = RD->getAttr()) if (!A->isImplicit()) { Availability.Unavailable = true; Availability.UnconditionallyUnavailable = true; } if (const auto *A = RD->getAttr()) if (!A->isImplicit()) Availability.UnconditionallyDeprecated = true; } return Availability; } ASTContext &Context; APISet API; }; class ExtractAPIConsumer : public ASTConsumer { public: ExtractAPIConsumer(ASTContext &Context, StringRef ProductName, std::unique_ptr OS) : Visitor(Context), ProductName(ProductName), OS(std::move(OS)) {} void HandleTranslationUnit(ASTContext &Context) override { // Use ExtractAPIVisitor to traverse symbol declarations in the context. Visitor.TraverseDecl(Context.getTranslationUnitDecl()); // Setup a SymbolGraphSerializer to write out collected API information in // the Symbol Graph format. // FIXME: Make the kind of APISerializer configurable. SymbolGraphSerializer SGSerializer(Visitor.getAPI(), ProductName); SGSerializer.serialize(*OS); } private: ExtractAPIVisitor Visitor; std::string ProductName; std::unique_ptr OS; }; } // namespace std::unique_ptr ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { std::unique_ptr OS = CreateOutputFile(CI, InFile); if (!OS) return nullptr; return std::make_unique( CI.getASTContext(), CI.getInvocation().getFrontendOpts().ProductName, std::move(OS)); } std::unique_ptr ExtractAPIAction::CreateOutputFile(CompilerInstance &CI, StringRef InFile) { std::unique_ptr OS = CI.createDefaultOutputFile(/*Binary=*/false, InFile, /*Extension=*/"json", /*RemoveFileOnSignal=*/false); if (!OS) return nullptr; return OS; }