mirror of
https://github.com/llvm/llvm-project.git
synced 2025-05-08 05:56:06 +00:00

Typedef records consist of the symbol associated with the underlying TypedefDecl and a SymbolReference to the underlying type. Additionally typedefs for anonymous TagTypes use the typedef'd name as the symbol name in their respective records and USRs. As a result the declaration fragments for the anonymous TagType are those for the associated typedef. This means that when the user is defining a typedef to a typedef to a anonymous type, we use a reference the anonymous TagType itself and do not emit the typedef to the anonymous type in the generated symbol graph, including in the type destination of further typedef symbol records. Differential Revision: https://reviews.llvm.org/D123019
698 lines
26 KiB
C++
698 lines
26 KiB
C++
//===- 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 "TypedefUnderlyingTypeResolver.h"
|
|
#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 "clang/Frontend/FrontendOptions.h"
|
|
#include "clang/Lex/MacroInfo.h"
|
|
#include "clang/Lex/PPCallbacks.h"
|
|
#include "clang/Lex/PreprocessorOptions.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/Support/MemoryBuffer.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
using namespace clang;
|
|
using namespace extractapi;
|
|
|
|
namespace {
|
|
|
|
StringRef getTypedefName(const TagDecl *Decl) {
|
|
if (const auto *TypedefDecl = Decl->getTypedefNameForAnonDecl())
|
|
return TypedefDecl->getName();
|
|
|
|
return {};
|
|
}
|
|
|
|
/// The RecursiveASTVisitor to traverse symbol declarations and collect API
|
|
/// information.
|
|
class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
|
|
public:
|
|
ExtractAPIVisitor(ASTContext &Context, APISet &API)
|
|
: Context(Context), API(API) {}
|
|
|
|
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;
|
|
}
|
|
|
|
bool VisitEnumDecl(const EnumDecl *Decl) {
|
|
if (!Decl->isComplete())
|
|
return true;
|
|
|
|
// Skip forward declaration.
|
|
if (!Decl->isThisDeclarationADefinition())
|
|
return true;
|
|
|
|
// Collect symbol information.
|
|
StringRef Name = Decl->getName();
|
|
if (Name.empty())
|
|
Name = getTypedefName(Decl);
|
|
StringRef USR = API.recordUSR(Decl);
|
|
PresumedLoc Loc =
|
|
Context.getSourceManager().getPresumedLoc(Decl->getLocation());
|
|
AvailabilityInfo Availability = getAvailability(Decl);
|
|
DocComment Comment;
|
|
if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
|
|
Comment = RawComment->getFormattedLines(Context.getSourceManager(),
|
|
Context.getDiagnostics());
|
|
|
|
// Build declaration fragments and sub-heading for the enum.
|
|
DeclarationFragments Declaration =
|
|
DeclarationFragmentsBuilder::getFragmentsForEnum(Decl);
|
|
DeclarationFragments SubHeading =
|
|
DeclarationFragmentsBuilder::getSubHeading(Decl);
|
|
|
|
EnumRecord *EnumRecord = API.addEnum(Name, USR, Loc, Availability, Comment,
|
|
Declaration, SubHeading);
|
|
|
|
// Now collect information about the enumerators in this enum.
|
|
recordEnumConstants(EnumRecord, Decl->enumerators());
|
|
|
|
return true;
|
|
}
|
|
|
|
bool VisitRecordDecl(const RecordDecl *Decl) {
|
|
if (!Decl->isCompleteDefinition())
|
|
return true;
|
|
|
|
// Skip C++ structs/classes/unions
|
|
// TODO: support C++ records
|
|
if (isa<CXXRecordDecl>(Decl))
|
|
return true;
|
|
|
|
// Collect symbol information.
|
|
StringRef Name = Decl->getName();
|
|
if (Name.empty())
|
|
Name = getTypedefName(Decl);
|
|
StringRef USR = API.recordUSR(Decl);
|
|
PresumedLoc Loc =
|
|
Context.getSourceManager().getPresumedLoc(Decl->getLocation());
|
|
AvailabilityInfo Availability = getAvailability(Decl);
|
|
DocComment Comment;
|
|
if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
|
|
Comment = RawComment->getFormattedLines(Context.getSourceManager(),
|
|
Context.getDiagnostics());
|
|
|
|
// Build declaration fragments and sub-heading for the struct.
|
|
DeclarationFragments Declaration =
|
|
DeclarationFragmentsBuilder::getFragmentsForStruct(Decl);
|
|
DeclarationFragments SubHeading =
|
|
DeclarationFragmentsBuilder::getSubHeading(Decl);
|
|
|
|
StructRecord *StructRecord = API.addStruct(
|
|
Name, USR, Loc, Availability, Comment, Declaration, SubHeading);
|
|
|
|
// Now collect information about the fields in this struct.
|
|
recordStructFields(StructRecord, Decl->fields());
|
|
|
|
return true;
|
|
}
|
|
|
|
bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *Decl) {
|
|
// Skip forward declaration for classes (@class)
|
|
if (!Decl->isThisDeclarationADefinition())
|
|
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 interface.
|
|
DeclarationFragments Declaration =
|
|
DeclarationFragmentsBuilder::getFragmentsForObjCInterface(Decl);
|
|
DeclarationFragments SubHeading =
|
|
DeclarationFragmentsBuilder::getSubHeading(Decl);
|
|
|
|
// Collect super class information.
|
|
SymbolReference SuperClass;
|
|
if (const auto *SuperClassDecl = Decl->getSuperClass()) {
|
|
SuperClass.Name = SuperClassDecl->getObjCRuntimeNameAsString();
|
|
SuperClass.USR = API.recordUSR(SuperClassDecl);
|
|
}
|
|
|
|
ObjCInterfaceRecord *ObjCInterfaceRecord =
|
|
API.addObjCInterface(Name, USR, Loc, Availability, Linkage, Comment,
|
|
Declaration, SubHeading, SuperClass);
|
|
|
|
// Record all methods (selectors). This doesn't include automatically
|
|
// synthesized property methods.
|
|
recordObjCMethods(ObjCInterfaceRecord, Decl->methods());
|
|
recordObjCProperties(ObjCInterfaceRecord, Decl->properties());
|
|
recordObjCInstanceVariables(ObjCInterfaceRecord, Decl->ivars());
|
|
recordObjCProtocols(ObjCInterfaceRecord, Decl->protocols());
|
|
|
|
return true;
|
|
}
|
|
|
|
bool VisitObjCProtocolDecl(const ObjCProtocolDecl *Decl) {
|
|
// Skip forward declaration for protocols (@protocol).
|
|
if (!Decl->isThisDeclarationADefinition())
|
|
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);
|
|
DocComment Comment;
|
|
if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
|
|
Comment = RawComment->getFormattedLines(Context.getSourceManager(),
|
|
Context.getDiagnostics());
|
|
|
|
// Build declaration fragments and sub-heading for the protocol.
|
|
DeclarationFragments Declaration =
|
|
DeclarationFragmentsBuilder::getFragmentsForObjCProtocol(Decl);
|
|
DeclarationFragments SubHeading =
|
|
DeclarationFragmentsBuilder::getSubHeading(Decl);
|
|
|
|
ObjCProtocolRecord *ObjCProtocolRecord = API.addObjCProtocol(
|
|
Name, USR, Loc, Availability, Comment, Declaration, SubHeading);
|
|
|
|
recordObjCMethods(ObjCProtocolRecord, Decl->methods());
|
|
recordObjCProperties(ObjCProtocolRecord, Decl->properties());
|
|
recordObjCProtocols(ObjCProtocolRecord, Decl->protocols());
|
|
|
|
return true;
|
|
}
|
|
|
|
bool VisitTypedefNameDecl(const TypedefNameDecl *Decl) {
|
|
// Skip ObjC Type Parameter for now.
|
|
if (isa<ObjCTypeParamDecl>(Decl))
|
|
return true;
|
|
|
|
if (!Decl->isDefinedOutsideFunctionOrMethod())
|
|
return true;
|
|
|
|
PresumedLoc Loc =
|
|
Context.getSourceManager().getPresumedLoc(Decl->getLocation());
|
|
StringRef Name = Decl->getName();
|
|
AvailabilityInfo Availability = getAvailability(Decl);
|
|
StringRef USR = API.recordUSR(Decl);
|
|
DocComment Comment;
|
|
if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
|
|
Comment = RawComment->getFormattedLines(Context.getSourceManager(),
|
|
Context.getDiagnostics());
|
|
|
|
QualType Type = Decl->getUnderlyingType();
|
|
SymbolReference SymRef =
|
|
TypedefUnderlyingTypeResolver(Context).getSymbolReferenceForType(Type,
|
|
API);
|
|
|
|
API.addTypedef(Name, USR, Loc, Availability, Comment,
|
|
DeclarationFragmentsBuilder::getFragmentsForTypedef(Decl),
|
|
DeclarationFragmentsBuilder::getSubHeading(Decl), SymRef);
|
|
|
|
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;
|
|
}
|
|
|
|
/// Collect API information for the enum constants and associate with the
|
|
/// parent enum.
|
|
void recordEnumConstants(EnumRecord *EnumRecord,
|
|
const EnumDecl::enumerator_range Constants) {
|
|
for (const auto *Constant : Constants) {
|
|
// Collect symbol information.
|
|
StringRef Name = Constant->getName();
|
|
StringRef USR = API.recordUSR(Constant);
|
|
PresumedLoc Loc =
|
|
Context.getSourceManager().getPresumedLoc(Constant->getLocation());
|
|
AvailabilityInfo Availability = getAvailability(Constant);
|
|
DocComment Comment;
|
|
if (auto *RawComment = Context.getRawCommentForDeclNoCache(Constant))
|
|
Comment = RawComment->getFormattedLines(Context.getSourceManager(),
|
|
Context.getDiagnostics());
|
|
|
|
// Build declaration fragments and sub-heading for the enum constant.
|
|
DeclarationFragments Declaration =
|
|
DeclarationFragmentsBuilder::getFragmentsForEnumConstant(Constant);
|
|
DeclarationFragments SubHeading =
|
|
DeclarationFragmentsBuilder::getSubHeading(Constant);
|
|
|
|
API.addEnumConstant(EnumRecord, Name, USR, Loc, Availability, Comment,
|
|
Declaration, SubHeading);
|
|
}
|
|
}
|
|
|
|
/// Collect API information for the struct fields and associate with the
|
|
/// parent struct.
|
|
void recordStructFields(StructRecord *StructRecord,
|
|
const RecordDecl::field_range Fields) {
|
|
for (const auto *Field : Fields) {
|
|
// Collect symbol information.
|
|
StringRef Name = Field->getName();
|
|
StringRef USR = API.recordUSR(Field);
|
|
PresumedLoc Loc =
|
|
Context.getSourceManager().getPresumedLoc(Field->getLocation());
|
|
AvailabilityInfo Availability = getAvailability(Field);
|
|
DocComment Comment;
|
|
if (auto *RawComment = Context.getRawCommentForDeclNoCache(Field))
|
|
Comment = RawComment->getFormattedLines(Context.getSourceManager(),
|
|
Context.getDiagnostics());
|
|
|
|
// Build declaration fragments and sub-heading for the struct field.
|
|
DeclarationFragments Declaration =
|
|
DeclarationFragmentsBuilder::getFragmentsForField(Field);
|
|
DeclarationFragments SubHeading =
|
|
DeclarationFragmentsBuilder::getSubHeading(Field);
|
|
|
|
API.addStructField(StructRecord, Name, USR, Loc, Availability, Comment,
|
|
Declaration, SubHeading);
|
|
}
|
|
}
|
|
|
|
/// Collect API information for the Objective-C methods and associate with the
|
|
/// parent container.
|
|
void recordObjCMethods(ObjCContainerRecord *Container,
|
|
const ObjCContainerDecl::method_range Methods) {
|
|
for (const auto *Method : Methods) {
|
|
// Don't record selectors for properties.
|
|
if (Method->isPropertyAccessor())
|
|
continue;
|
|
|
|
StringRef Name = API.copyString(Method->getSelector().getAsString());
|
|
StringRef USR = API.recordUSR(Method);
|
|
PresumedLoc Loc =
|
|
Context.getSourceManager().getPresumedLoc(Method->getLocation());
|
|
AvailabilityInfo Availability = getAvailability(Method);
|
|
DocComment Comment;
|
|
if (auto *RawComment = Context.getRawCommentForDeclNoCache(Method))
|
|
Comment = RawComment->getFormattedLines(Context.getSourceManager(),
|
|
Context.getDiagnostics());
|
|
|
|
// Build declaration fragments, sub-heading, and signature for the method.
|
|
DeclarationFragments Declaration =
|
|
DeclarationFragmentsBuilder::getFragmentsForObjCMethod(Method);
|
|
DeclarationFragments SubHeading =
|
|
DeclarationFragmentsBuilder::getSubHeading(Method);
|
|
FunctionSignature Signature =
|
|
DeclarationFragmentsBuilder::getFunctionSignature(Method);
|
|
|
|
API.addObjCMethod(Container, Name, USR, Loc, Availability, Comment,
|
|
Declaration, SubHeading, Signature,
|
|
Method->isInstanceMethod());
|
|
}
|
|
}
|
|
|
|
void recordObjCProperties(ObjCContainerRecord *Container,
|
|
const ObjCContainerDecl::prop_range Properties) {
|
|
for (const auto *Property : Properties) {
|
|
StringRef Name = Property->getName();
|
|
StringRef USR = API.recordUSR(Property);
|
|
PresumedLoc Loc =
|
|
Context.getSourceManager().getPresumedLoc(Property->getLocation());
|
|
AvailabilityInfo Availability = getAvailability(Property);
|
|
DocComment Comment;
|
|
if (auto *RawComment = Context.getRawCommentForDeclNoCache(Property))
|
|
Comment = RawComment->getFormattedLines(Context.getSourceManager(),
|
|
Context.getDiagnostics());
|
|
|
|
// Build declaration fragments and sub-heading for the property.
|
|
DeclarationFragments Declaration =
|
|
DeclarationFragmentsBuilder::getFragmentsForObjCProperty(Property);
|
|
DeclarationFragments SubHeading =
|
|
DeclarationFragmentsBuilder::getSubHeading(Property);
|
|
|
|
StringRef GetterName =
|
|
API.copyString(Property->getGetterName().getAsString());
|
|
StringRef SetterName =
|
|
API.copyString(Property->getSetterName().getAsString());
|
|
|
|
// Get the attributes for property.
|
|
unsigned Attributes = ObjCPropertyRecord::NoAttr;
|
|
if (Property->getPropertyAttributes() &
|
|
ObjCPropertyAttribute::kind_readonly)
|
|
Attributes |= ObjCPropertyRecord::ReadOnly;
|
|
if (Property->getPropertyAttributes() & ObjCPropertyAttribute::kind_class)
|
|
Attributes |= ObjCPropertyRecord::Class;
|
|
|
|
API.addObjCProperty(
|
|
Container, Name, USR, Loc, Availability, Comment, Declaration,
|
|
SubHeading,
|
|
static_cast<ObjCPropertyRecord::AttributeKind>(Attributes),
|
|
GetterName, SetterName, Property->isOptional());
|
|
}
|
|
}
|
|
|
|
void recordObjCInstanceVariables(
|
|
ObjCContainerRecord *Container,
|
|
const llvm::iterator_range<
|
|
DeclContext::specific_decl_iterator<ObjCIvarDecl>>
|
|
Ivars) {
|
|
for (const auto *Ivar : Ivars) {
|
|
StringRef Name = Ivar->getName();
|
|
StringRef USR = API.recordUSR(Ivar);
|
|
PresumedLoc Loc =
|
|
Context.getSourceManager().getPresumedLoc(Ivar->getLocation());
|
|
AvailabilityInfo Availability = getAvailability(Ivar);
|
|
DocComment Comment;
|
|
if (auto *RawComment = Context.getRawCommentForDeclNoCache(Ivar))
|
|
Comment = RawComment->getFormattedLines(Context.getSourceManager(),
|
|
Context.getDiagnostics());
|
|
|
|
// Build declaration fragments and sub-heading for the instance variable.
|
|
DeclarationFragments Declaration =
|
|
DeclarationFragmentsBuilder::getFragmentsForField(Ivar);
|
|
DeclarationFragments SubHeading =
|
|
DeclarationFragmentsBuilder::getSubHeading(Ivar);
|
|
|
|
ObjCInstanceVariableRecord::AccessControl Access =
|
|
Ivar->getCanonicalAccessControl();
|
|
|
|
API.addObjCInstanceVariable(Container, Name, USR, Loc, Availability,
|
|
Comment, Declaration, SubHeading, Access);
|
|
}
|
|
}
|
|
|
|
void recordObjCProtocols(ObjCContainerRecord *Container,
|
|
ObjCInterfaceDecl::protocol_range Protocols) {
|
|
for (const auto *Protocol : Protocols)
|
|
Container->Protocols.emplace_back(Protocol->getName(),
|
|
API.recordUSR(Protocol));
|
|
}
|
|
|
|
ASTContext &Context;
|
|
APISet &API;
|
|
};
|
|
|
|
class ExtractAPIConsumer : public ASTConsumer {
|
|
public:
|
|
ExtractAPIConsumer(ASTContext &Context, APISet &API)
|
|
: Visitor(Context, API) {}
|
|
|
|
void HandleTranslationUnit(ASTContext &Context) override {
|
|
// Use ExtractAPIVisitor to traverse symbol declarations in the context.
|
|
Visitor.TraverseDecl(Context.getTranslationUnitDecl());
|
|
}
|
|
|
|
private:
|
|
ExtractAPIVisitor Visitor;
|
|
};
|
|
|
|
class MacroCallback : public PPCallbacks {
|
|
public:
|
|
MacroCallback(const SourceManager &SM, APISet &API) : SM(SM), API(API) {}
|
|
|
|
void MacroDefined(const Token &MacroNameToken,
|
|
const MacroDirective *MD) override {
|
|
auto *MacroInfo = MD->getMacroInfo();
|
|
|
|
if (MacroInfo->isBuiltinMacro())
|
|
return;
|
|
|
|
auto SourceLoc = MacroNameToken.getLocation();
|
|
if (SM.isWrittenInBuiltinFile(SourceLoc) ||
|
|
SM.isWrittenInCommandLineFile(SourceLoc))
|
|
return;
|
|
|
|
PendingMacros.emplace_back(MacroNameToken, MD);
|
|
}
|
|
|
|
// If a macro gets undefined at some point during preprocessing of the inputs
|
|
// it means that it isn't an exposed API and we should therefore not add a
|
|
// macro definition for it.
|
|
void MacroUndefined(const Token &MacroNameToken, const MacroDefinition &MD,
|
|
const MacroDirective *Undef) override {
|
|
// If this macro wasn't previously defined we don't need to do anything
|
|
// here.
|
|
if (!Undef)
|
|
return;
|
|
|
|
llvm::erase_if(PendingMacros, [&MD](const PendingMacro &PM) {
|
|
return MD.getMacroInfo()->getDefinitionLoc() ==
|
|
PM.MD->getMacroInfo()->getDefinitionLoc();
|
|
});
|
|
}
|
|
|
|
void EndOfMainFile() override {
|
|
for (auto &PM : PendingMacros) {
|
|
// `isUsedForHeaderGuard` is only set when the preprocessor leaves the
|
|
// file so check for it here.
|
|
if (PM.MD->getMacroInfo()->isUsedForHeaderGuard())
|
|
continue;
|
|
|
|
StringRef Name = PM.MacroNameToken.getIdentifierInfo()->getName();
|
|
PresumedLoc Loc = SM.getPresumedLoc(PM.MacroNameToken.getLocation());
|
|
StringRef USR =
|
|
API.recordUSRForMacro(Name, PM.MacroNameToken.getLocation(), SM);
|
|
|
|
API.addMacroDefinition(
|
|
Name, USR, Loc,
|
|
DeclarationFragmentsBuilder::getFragmentsForMacro(Name, PM.MD),
|
|
DeclarationFragmentsBuilder::getSubHeadingForMacro(Name));
|
|
}
|
|
|
|
PendingMacros.clear();
|
|
}
|
|
|
|
private:
|
|
struct PendingMacro {
|
|
Token MacroNameToken;
|
|
const MacroDirective *MD;
|
|
|
|
PendingMacro(const Token &MacroNameToken, const MacroDirective *MD)
|
|
: MacroNameToken(MacroNameToken), MD(MD) {}
|
|
};
|
|
|
|
const SourceManager &SM;
|
|
APISet &API;
|
|
llvm::SmallVector<PendingMacro> PendingMacros;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
std::unique_ptr<ASTConsumer>
|
|
ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
|
|
OS = CreateOutputFile(CI, InFile);
|
|
if (!OS)
|
|
return nullptr;
|
|
|
|
ProductName = CI.getFrontendOpts().ProductName;
|
|
|
|
// Now that we have enough information about the language options and the
|
|
// target triple, let's create the APISet before anyone uses it.
|
|
API = std::make_unique<APISet>(
|
|
CI.getTarget().getTriple(),
|
|
CI.getFrontendOpts().Inputs.back().getKind().getLanguage());
|
|
|
|
// Register preprocessor callbacks that will add macro definitions to API.
|
|
CI.getPreprocessor().addPPCallbacks(
|
|
std::make_unique<MacroCallback>(CI.getSourceManager(), *API));
|
|
|
|
return std::make_unique<ExtractAPIConsumer>(CI.getASTContext(), *API);
|
|
}
|
|
|
|
bool ExtractAPIAction::PrepareToExecuteAction(CompilerInstance &CI) {
|
|
auto &Inputs = CI.getFrontendOpts().Inputs;
|
|
if (Inputs.empty())
|
|
return true;
|
|
|
|
auto Kind = Inputs[0].getKind();
|
|
|
|
// Convert the header file inputs into a single input buffer.
|
|
SmallString<256> HeaderContents;
|
|
for (const FrontendInputFile &FIF : Inputs) {
|
|
if (Kind.isObjectiveC())
|
|
HeaderContents += "#import";
|
|
else
|
|
HeaderContents += "#include";
|
|
HeaderContents += " \"";
|
|
HeaderContents += FIF.getFile();
|
|
HeaderContents += "\"\n";
|
|
}
|
|
|
|
Buffer = llvm::MemoryBuffer::getMemBufferCopy(HeaderContents,
|
|
getInputBufferName());
|
|
|
|
// Set that buffer up as our "real" input in the CompilerInstance.
|
|
Inputs.clear();
|
|
Inputs.emplace_back(Buffer->getMemBufferRef(), Kind, /*IsSystem*/ false);
|
|
|
|
return true;
|
|
}
|
|
|
|
void ExtractAPIAction::EndSourceFileAction() {
|
|
if (!OS)
|
|
return;
|
|
|
|
// Setup a SymbolGraphSerializer to write out collected API information in
|
|
// the Symbol Graph format.
|
|
// FIXME: Make the kind of APISerializer configurable.
|
|
SymbolGraphSerializer SGSerializer(*API, ProductName);
|
|
SGSerializer.serialize(*OS);
|
|
OS->flush();
|
|
}
|
|
|
|
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;
|
|
}
|