2022-03-21 00:53:28 -07:00
|
|
|
//===- ExtractAPI/ExtractAPIConsumer.cpp ------------------------*- C++ -*-===//
|
2022-02-10 13:42:35 -08:00
|
|
|
//
|
|
|
|
// 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
|
2022-03-21 00:53:28 -07:00
|
|
|
/// This file implements the ExtractAPIAction, and ASTVisitor/Consumer to
|
|
|
|
/// collect API information.
|
2022-02-10 13:42:35 -08:00
|
|
|
///
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#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"
|
2022-03-21 00:53:28 -07:00
|
|
|
#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"
|
2022-02-10 13:42:35 -08:00
|
|
|
#include "clang/Frontend/ASTConsumers.h"
|
|
|
|
#include "clang/Frontend/CompilerInstance.h"
|
|
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
|
|
|
|
using namespace clang;
|
2022-03-21 00:53:28 -07:00
|
|
|
using namespace extractapi;
|
2022-02-10 13:42:35 -08:00
|
|
|
|
|
|
|
namespace {
|
2022-03-21 00:53:28 -07:00
|
|
|
|
|
|
|
/// The RecursiveASTVisitor to traverse symbol declarations and collect API
|
|
|
|
/// information.
|
2022-02-10 13:42:35 -08:00
|
|
|
class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
|
|
|
|
public:
|
|
|
|
explicit ExtractAPIVisitor(ASTContext &Context)
|
|
|
|
: Context(Context),
|
|
|
|
API(Context.getTargetInfo().getTriple(), Context.getLangOpts()) {}
|
|
|
|
|
2022-03-16 16:01:50 -07:00
|
|
|
const APISet &getAPI() const { return API; }
|
2022-02-10 13:42:35 -08:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2022-03-21 00:53:28 -07:00
|
|
|
// Collect symbol information.
|
2022-02-10 13:42:35 -08:00
|
|
|
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());
|
2022-03-21 00:53:28 -07:00
|
|
|
|
|
|
|
// Build declaration fragments and sub-heading for the variable.
|
2022-02-10 13:42:35 -08:00
|
|
|
DeclarationFragments Declaration =
|
|
|
|
DeclarationFragmentsBuilder::getFragmentsForVar(Decl);
|
|
|
|
DeclarationFragments SubHeading =
|
|
|
|
DeclarationFragmentsBuilder::getSubHeading(Decl);
|
|
|
|
|
2022-03-21 00:53:28 -07:00
|
|
|
// Add the global variable record to the API set.
|
2022-02-10 13:42:35 -08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2022-03-21 00:53:28 -07:00
|
|
|
// Collect symbol information.
|
2022-02-10 13:42:35 -08:00
|
|
|
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());
|
2022-03-21 00:53:28 -07:00
|
|
|
|
|
|
|
// Build declaration fragments, sub-heading, and signature of the function.
|
2022-02-10 13:42:35 -08:00
|
|
|
DeclarationFragments Declaration =
|
|
|
|
DeclarationFragmentsBuilder::getFragmentsForFunction(Decl);
|
|
|
|
DeclarationFragments SubHeading =
|
|
|
|
DeclarationFragmentsBuilder::getSubHeading(Decl);
|
|
|
|
FunctionSignature Signature =
|
|
|
|
DeclarationFragmentsBuilder::getFunctionSignature(Decl);
|
|
|
|
|
2022-03-21 00:53:28 -07:00
|
|
|
// Add the function record to the API set.
|
2022-02-10 13:42:35 -08:00
|
|
|
API.addFunction(Name, USR, Loc, Availability, Linkage, Comment, Declaration,
|
|
|
|
SubHeading, Signature);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2022-03-21 00:53:28 -07:00
|
|
|
/// Get availability information of the declaration \p D.
|
2022-02-10 13:42:35 -08:00
|
|
|
AvailabilityInfo getAvailability(const Decl *D) const {
|
|
|
|
StringRef PlatformName = Context.getTargetInfo().getPlatformName();
|
|
|
|
|
|
|
|
AvailabilityInfo Availability;
|
2022-03-21 00:53:28 -07:00
|
|
|
// Collect availability attributes from all redeclarations.
|
2022-02-10 13:42:35 -08:00
|
|
|
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;
|
2022-03-16 16:01:50 -07:00
|
|
|
APISet API;
|
2022-02-10 13:42:35 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
class ExtractAPIConsumer : public ASTConsumer {
|
|
|
|
public:
|
2022-03-16 13:38:54 +00:00
|
|
|
ExtractAPIConsumer(ASTContext &Context, StringRef ProductName,
|
|
|
|
std::unique_ptr<raw_pwrite_stream> OS)
|
|
|
|
: Visitor(Context), ProductName(ProductName), OS(std::move(OS)) {}
|
2022-02-10 13:42:35 -08:00
|
|
|
|
|
|
|
void HandleTranslationUnit(ASTContext &Context) override {
|
2022-03-21 00:53:28 -07:00
|
|
|
// Use ExtractAPIVisitor to traverse symbol declarations in the context.
|
2022-02-10 13:42:35 -08:00
|
|
|
Visitor.TraverseDecl(Context.getTranslationUnitDecl());
|
2022-03-21 00:53:28 -07:00
|
|
|
|
|
|
|
// Setup a SymbolGraphSerializer to write out collected API information in
|
|
|
|
// the Symbol Graph format.
|
|
|
|
// FIXME: Make the kind of APISerializer configurable.
|
2022-03-16 13:38:54 +00:00
|
|
|
SymbolGraphSerializer SGSerializer(Visitor.getAPI(), ProductName);
|
2022-03-21 00:53:28 -07:00
|
|
|
SGSerializer.serialize(*OS);
|
2022-02-10 13:42:35 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
ExtractAPIVisitor Visitor;
|
2022-03-16 13:38:54 +00:00
|
|
|
std::string ProductName;
|
2022-02-10 13:42:35 -08:00
|
|
|
std::unique_ptr<raw_pwrite_stream> OS;
|
|
|
|
};
|
2022-03-21 00:53:28 -07:00
|
|
|
|
2022-02-10 13:42:35 -08:00
|
|
|
} // 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;
|
2022-03-16 13:38:54 +00:00
|
|
|
return std::make_unique<ExtractAPIConsumer>(
|
|
|
|
CI.getASTContext(), CI.getInvocation().getFrontendOpts().ProductName,
|
|
|
|
std::move(OS));
|
2022-02-10 13:42:35 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|