aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
diff options
context:
space:
mode:
authorZixu Wang <zixu_wang@apple.com>2022-03-21 00:53:28 -0700
committerZixu Wang <zixu_wang@apple.com>2022-03-22 13:21:57 -0700
commit89f6b26f1beb2c1344f5cfeb34e405128544c76b (patch)
tree06b93d32cadc4163d407cc6ac7374b32c311a44d /clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
parent57d02900b54bf162ec476da2ce2bd893dcdbe24b (diff)
downloadllvm-89f6b26f1beb2c1344f5cfeb34e405128544c76b.zip
llvm-89f6b26f1beb2c1344f5cfeb34e405128544c76b.tar.gz
llvm-89f6b26f1beb2c1344f5cfeb34e405128544c76b.tar.bz2
[clang][extract-api] Refactor ExtractAPI and improve docs
- The name SymbolGraph is inappropriate and confusing for the new library for clang-extract-api. Refactor and rename things to make it clear that ExtractAPI is the core functionality and SymbolGraph is one serializer for the API information. - Add documentation comments to ExtractAPI classes and methods to improve readability and clearness of the ExtractAPI work. Differential Revision: https://reviews.llvm.org/D122160
Diffstat (limited to 'clang/lib/ExtractAPI/ExtractAPIConsumer.cpp')
-rw-r--r--clang/lib/ExtractAPI/ExtractAPIConsumer.cpp224
1 files changed, 224 insertions, 0 deletions
diff --git a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
new file mode 100644
index 0000000..c469f56
--- /dev/null
+++ b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
@@ -0,0 +1,224 @@
+//===- 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<ExtractAPIVisitor> {
+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<ParmVarDecl>(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<CXXMethodDecl>(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<CXXRecordDecl>())
+ return true;
+ }
+
+ // Skip ConstructorDecl and DestructorDecl.
+ if (isa<CXXConstructorDecl>(Method) || isa<CXXDestructorDecl>(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<AvailabilityAttr>()) {
+ 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<UnavailableAttr>())
+ if (!A->isImplicit()) {
+ Availability.Unavailable = true;
+ Availability.UnconditionallyUnavailable = true;
+ }
+
+ if (const auto *A = RD->getAttr<DeprecatedAttr>())
+ if (!A->isImplicit())
+ Availability.UnconditionallyDeprecated = true;
+ }
+
+ return Availability;
+ }
+
+ ASTContext &Context;
+ APISet API;
+};
+
+class ExtractAPIConsumer : public ASTConsumer {
+public:
+ ExtractAPIConsumer(ASTContext &Context, std::unique_ptr<raw_pwrite_stream> OS)
+ : Visitor(Context), 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());
+ SGSerializer.serialize(*OS);
+ }
+
+private:
+ ExtractAPIVisitor Visitor;
+ std::unique_ptr<raw_pwrite_stream> OS;
+};
+
+} // namespace
+
+std::unique_ptr<ASTConsumer>
+ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
+ std::unique_ptr<raw_pwrite_stream> OS = CreateOutputFile(CI, InFile);
+ if (!OS)
+ return nullptr;
+ return std::make_unique<ExtractAPIConsumer>(CI.getASTContext(),
+ std::move(OS));
+}
+
+std::unique_ptr<raw_pwrite_stream>
+ExtractAPIAction::CreateOutputFile(CompilerInstance &CI, StringRef InFile) {
+ std::unique_ptr<raw_pwrite_stream> OS =
+ CI.createDefaultOutputFile(/*Binary=*/false, InFile, /*Extension=*/"json",
+ /*RemoveFileOnSignal=*/false);
+ if (!OS)
+ return nullptr;
+ return OS;
+}