2024-06-13 20:20:27 +01:00

728 lines
24 KiB
C++

//===- Visitor.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
//
//===----------------------------------------------------------------------===//
#include "clang/InstallAPI/Visitor.h"
#include "clang/AST/Availability.h"
#include "clang/AST/ParentMapContext.h"
#include "clang/AST/VTableBuilder.h"
#include "clang/Basic/Linkage.h"
#include "clang/InstallAPI/DylibVerifier.h"
#include "clang/InstallAPI/FrontendRecords.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/Mangler.h"
using namespace llvm;
using namespace llvm::MachO;
namespace {
enum class CXXLinkage {
ExternalLinkage,
LinkOnceODRLinkage,
WeakODRLinkage,
PrivateLinkage,
};
}
namespace clang::installapi {
// Exported NamedDecl needs to have external linkage and
// default visibility from LinkageComputer.
static bool isExported(const NamedDecl *D) {
auto LV = D->getLinkageAndVisibility();
return isExternallyVisible(LV.getLinkage()) &&
(LV.getVisibility() == DefaultVisibility);
}
static bool isInlined(const FunctionDecl *D) {
bool HasInlineAttribute = false;
bool NoCXXAttr =
(!D->getASTContext().getLangOpts().CPlusPlus &&
!D->getASTContext().getTargetInfo().getCXXABI().isMicrosoft() &&
!D->hasAttr<DLLExportAttr>());
// Check all redeclarations to find an inline attribute or keyword.
for (const auto *RD : D->redecls()) {
if (!RD->isInlined())
continue;
HasInlineAttribute = true;
if (!(NoCXXAttr || RD->hasAttr<GNUInlineAttr>()))
continue;
if (RD->doesThisDeclarationHaveABody() &&
RD->isInlineDefinitionExternallyVisible())
return false;
}
if (!HasInlineAttribute)
return false;
return true;
}
static SymbolFlags getFlags(bool WeakDef, bool ThreadLocal = false) {
SymbolFlags Result = SymbolFlags::None;
if (WeakDef)
Result |= SymbolFlags::WeakDefined;
if (ThreadLocal)
Result |= SymbolFlags::ThreadLocalValue;
return Result;
}
void InstallAPIVisitor::HandleTranslationUnit(ASTContext &ASTCtx) {
if (ASTCtx.getDiagnostics().hasErrorOccurred())
return;
auto *D = ASTCtx.getTranslationUnitDecl();
TraverseDecl(D);
}
std::string InstallAPIVisitor::getMangledName(const NamedDecl *D) const {
SmallString<256> Name;
if (MC->shouldMangleDeclName(D)) {
raw_svector_ostream NStream(Name);
MC->mangleName(D, NStream);
} else
Name += D->getNameAsString();
return getBackendMangledName(Name);
}
std::string InstallAPIVisitor::getBackendMangledName(Twine Name) const {
SmallString<256> FinalName;
Mangler::getNameWithPrefix(FinalName, Name, DataLayout(Layout));
return std::string(FinalName);
}
std::optional<HeaderType>
InstallAPIVisitor::getAccessForDecl(const NamedDecl *D) const {
SourceLocation Loc = D->getLocation();
if (Loc.isInvalid())
return std::nullopt;
// If the loc refers to a macro expansion, InstallAPI needs to first get the
// file location of the expansion.
auto FileLoc = SrcMgr.getFileLoc(Loc);
FileID ID = SrcMgr.getFileID(FileLoc);
if (ID.isInvalid())
return std::nullopt;
const FileEntry *FE = SrcMgr.getFileEntryForID(ID);
if (!FE)
return std::nullopt;
auto Header = Ctx.findAndRecordFile(FE, PP);
if (!Header.has_value())
return std::nullopt;
HeaderType Access = Header.value();
assert(Access != HeaderType::Unknown && "unexpected access level for global");
return Access;
}
/// Check if the interface itself or any of its super classes have an
/// exception attribute. InstallAPI needs to export an additional symbol
/// ("OBJC_EHTYPE_$CLASS_NAME") if any of the classes have the exception
/// attribute.
static bool hasObjCExceptionAttribute(const ObjCInterfaceDecl *D) {
for (; D != nullptr; D = D->getSuperClass())
if (D->hasAttr<ObjCExceptionAttr>())
return true;
return false;
}
void InstallAPIVisitor::recordObjCInstanceVariables(
const ASTContext &ASTCtx, ObjCContainerRecord *Record, StringRef SuperClass,
const llvm::iterator_range<
DeclContext::specific_decl_iterator<ObjCIvarDecl>>
Ivars) {
RecordLinkage Linkage = RecordLinkage::Exported;
const RecordLinkage ContainerLinkage = Record->getLinkage();
// If fragile, set to unknown.
if (ASTCtx.getLangOpts().ObjCRuntime.isFragile())
Linkage = RecordLinkage::Unknown;
// Linkage should be inherited from container.
else if (ContainerLinkage != RecordLinkage::Unknown)
Linkage = ContainerLinkage;
for (const auto *IV : Ivars) {
auto Access = getAccessForDecl(IV);
if (!Access)
continue;
StringRef Name = IV->getName();
const AvailabilityInfo Avail = AvailabilityInfo::createFromDecl(IV);
auto AC = IV->getCanonicalAccessControl();
auto [ObjCIVR, FA] =
Ctx.Slice->addObjCIVar(Record, Name, Linkage, Avail, IV, *Access, AC);
Ctx.Verifier->verify(ObjCIVR, FA, SuperClass);
}
}
bool InstallAPIVisitor::VisitObjCInterfaceDecl(const ObjCInterfaceDecl *D) {
// Skip forward declaration for classes (@class)
if (!D->isThisDeclarationADefinition())
return true;
// Skip over declarations that access could not be collected for.
auto Access = getAccessForDecl(D);
if (!Access)
return true;
StringRef Name = D->getObjCRuntimeNameAsString();
const RecordLinkage Linkage =
isExported(D) ? RecordLinkage::Exported : RecordLinkage::Internal;
const AvailabilityInfo Avail = AvailabilityInfo::createFromDecl(D);
const bool IsEHType =
(!D->getASTContext().getLangOpts().ObjCRuntime.isFragile() &&
hasObjCExceptionAttribute(D));
auto [Class, FA] =
Ctx.Slice->addObjCInterface(Name, Linkage, Avail, D, *Access, IsEHType);
Ctx.Verifier->verify(Class, FA);
// Get base class.
StringRef SuperClassName;
if (const auto *SuperClass = D->getSuperClass())
SuperClassName = SuperClass->getObjCRuntimeNameAsString();
recordObjCInstanceVariables(D->getASTContext(), Class, Class->getName(),
D->ivars());
return true;
}
bool InstallAPIVisitor::VisitObjCCategoryDecl(const ObjCCategoryDecl *D) {
StringRef CategoryName = D->getName();
// Skip over declarations that access could not be collected for.
auto Access = getAccessForDecl(D);
if (!Access)
return true;
const AvailabilityInfo Avail = AvailabilityInfo::createFromDecl(D);
const ObjCInterfaceDecl *InterfaceD = D->getClassInterface();
const StringRef InterfaceName = InterfaceD->getName();
ObjCCategoryRecord *CategoryRecord =
Ctx.Slice->addObjCCategory(InterfaceName, CategoryName, Avail, D, *Access)
.first;
recordObjCInstanceVariables(D->getASTContext(), CategoryRecord, InterfaceName,
D->ivars());
return true;
}
bool InstallAPIVisitor::VisitVarDecl(const VarDecl *D) {
// Skip function parameters.
if (isa<ParmVarDecl>(D))
return true;
// Skip variables in records. They are handled separately for C++.
if (D->getDeclContext()->isRecord())
return true;
// Skip anything inside functions or methods.
if (!D->isDefinedOutsideFunctionOrMethod())
return true;
// If this is a template but not specialization or instantiation, skip.
if (D->getASTContext().getTemplateOrSpecializationInfo(D) &&
D->getTemplateSpecializationKind() == TSK_Undeclared)
return true;
// Skip over declarations that access could not collected for.
auto Access = getAccessForDecl(D);
if (!Access)
return true;
const RecordLinkage Linkage =
isExported(D) ? RecordLinkage::Exported : RecordLinkage::Internal;
const bool WeakDef = D->hasAttr<WeakAttr>();
const bool ThreadLocal = D->getTLSKind() != VarDecl::TLS_None;
const AvailabilityInfo Avail = AvailabilityInfo::createFromDecl(D);
auto [GR, FA] = Ctx.Slice->addGlobal(getMangledName(D), Linkage,
GlobalRecord::Kind::Variable, Avail, D,
*Access, getFlags(WeakDef, ThreadLocal));
Ctx.Verifier->verify(GR, FA);
return true;
}
bool InstallAPIVisitor::VisitFunctionDecl(const FunctionDecl *D) {
if (const CXXMethodDecl *M = dyn_cast<CXXMethodDecl>(D)) {
// Skip member function in class templates.
if (M->getParent()->getDescribedClassTemplate() != nullptr)
return true;
// Skip methods in CXX RecordDecls.
for (const DynTypedNode &P : D->getASTContext().getParents(*M)) {
if (P.get<CXXRecordDecl>())
return true;
}
// Skip CXX ConstructorDecls and DestructorDecls.
if (isa<CXXConstructorDecl>(M) || isa<CXXDestructorDecl>(M))
return true;
}
// Skip templated functions.
switch (D->getTemplatedKind()) {
case FunctionDecl::TK_NonTemplate:
case FunctionDecl::TK_DependentNonTemplate:
break;
case FunctionDecl::TK_MemberSpecialization:
case FunctionDecl::TK_FunctionTemplateSpecialization:
if (auto *TempInfo = D->getTemplateSpecializationInfo()) {
if (!TempInfo->isExplicitInstantiationOrSpecialization())
return true;
}
break;
case FunctionDecl::TK_FunctionTemplate:
case FunctionDecl::TK_DependentFunctionTemplateSpecialization:
return true;
}
auto Access = getAccessForDecl(D);
if (!Access)
return true;
auto Name = getMangledName(D);
const AvailabilityInfo Avail = AvailabilityInfo::createFromDecl(D);
const bool ExplicitInstantiation = D->getTemplateSpecializationKind() ==
TSK_ExplicitInstantiationDeclaration;
const bool WeakDef = ExplicitInstantiation || D->hasAttr<WeakAttr>();
const bool Inlined = isInlined(D);
const RecordLinkage Linkage = (Inlined || !isExported(D))
? RecordLinkage::Internal
: RecordLinkage::Exported;
auto [GR, FA] =
Ctx.Slice->addGlobal(Name, Linkage, GlobalRecord::Kind::Function, Avail,
D, *Access, getFlags(WeakDef), Inlined);
Ctx.Verifier->verify(GR, FA);
return true;
}
static bool hasVTable(const CXXRecordDecl *D) {
// Check if vtable symbols should be emitted, only dynamic classes need
// vtables.
if (!D->hasDefinition() || !D->isDynamicClass())
return false;
assert(D->isExternallyVisible() && "Should be externally visible");
assert(D->isCompleteDefinition() && "Only works on complete definitions");
const CXXMethodDecl *KeyFunctionD =
D->getASTContext().getCurrentKeyFunction(D);
// If this class has a key function, then there is a vtable, possibly internal
// though.
if (KeyFunctionD) {
switch (KeyFunctionD->getTemplateSpecializationKind()) {
case TSK_Undeclared:
case TSK_ExplicitSpecialization:
case TSK_ImplicitInstantiation:
case TSK_ExplicitInstantiationDefinition:
return true;
case TSK_ExplicitInstantiationDeclaration:
llvm_unreachable(
"Unexpected TemplateSpecializationKind for key function");
}
} else if (D->isAbstract()) {
// If the class is abstract and it doesn't have a key function, it is a
// 'pure' virtual class. It doesn't need a vtable.
return false;
}
switch (D->getTemplateSpecializationKind()) {
case TSK_Undeclared:
case TSK_ExplicitSpecialization:
case TSK_ImplicitInstantiation:
return false;
case TSK_ExplicitInstantiationDeclaration:
case TSK_ExplicitInstantiationDefinition:
return true;
}
llvm_unreachable("Invalid TemplateSpecializationKind!");
}
static CXXLinkage getVTableLinkage(const CXXRecordDecl *D) {
assert((D->hasDefinition() && D->isDynamicClass()) && "Record has no vtable");
assert(D->isExternallyVisible() && "Record should be externally visible");
if (D->getVisibility() == HiddenVisibility)
return CXXLinkage::PrivateLinkage;
const CXXMethodDecl *KeyFunctionD =
D->getASTContext().getCurrentKeyFunction(D);
if (KeyFunctionD) {
// If this class has a key function, use that to determine the
// linkage of the vtable.
switch (KeyFunctionD->getTemplateSpecializationKind()) {
case TSK_Undeclared:
case TSK_ExplicitSpecialization:
if (isInlined(KeyFunctionD))
return CXXLinkage::LinkOnceODRLinkage;
return CXXLinkage::ExternalLinkage;
case TSK_ImplicitInstantiation:
llvm_unreachable("No external vtable for implicit instantiations");
case TSK_ExplicitInstantiationDefinition:
return CXXLinkage::WeakODRLinkage;
case TSK_ExplicitInstantiationDeclaration:
llvm_unreachable(
"Unexpected TemplateSpecializationKind for key function");
}
}
switch (D->getTemplateSpecializationKind()) {
case TSK_Undeclared:
case TSK_ExplicitSpecialization:
case TSK_ImplicitInstantiation:
return CXXLinkage::LinkOnceODRLinkage;
case TSK_ExplicitInstantiationDeclaration:
case TSK_ExplicitInstantiationDefinition:
return CXXLinkage::WeakODRLinkage;
}
llvm_unreachable("Invalid TemplateSpecializationKind!");
}
static bool isRTTIWeakDef(const CXXRecordDecl *D) {
if (D->hasAttr<WeakAttr>())
return true;
if (D->isAbstract() && D->getASTContext().getCurrentKeyFunction(D) == nullptr)
return true;
if (D->isDynamicClass())
return getVTableLinkage(D) != CXXLinkage::ExternalLinkage;
return false;
}
static bool hasRTTI(const CXXRecordDecl *D) {
if (!D->getASTContext().getLangOpts().RTTI)
return false;
if (!D->hasDefinition())
return false;
if (!D->isDynamicClass())
return false;
// Don't emit weak-def RTTI information. InstallAPI cannot reliably determine
// if the final binary will have those weak defined RTTI symbols. This depends
// on the optimization level and if the class has been instantiated and used.
//
// Luckily, the Apple static linker doesn't need those weak defined RTTI
// symbols for linking. They are only needed by the runtime linker. That means
// they can be safely dropped.
if (isRTTIWeakDef(D))
return false;
return true;
}
std::string
InstallAPIVisitor::getMangledCXXRTTIName(const CXXRecordDecl *D) const {
SmallString<256> Name;
raw_svector_ostream NameStream(Name);
MC->mangleCXXRTTIName(QualType(D->getTypeForDecl(), 0), NameStream);
return getBackendMangledName(Name);
}
std::string InstallAPIVisitor::getMangledCXXRTTI(const CXXRecordDecl *D) const {
SmallString<256> Name;
raw_svector_ostream NameStream(Name);
MC->mangleCXXRTTI(QualType(D->getTypeForDecl(), 0), NameStream);
return getBackendMangledName(Name);
}
std::string
InstallAPIVisitor::getMangledCXXVTableName(const CXXRecordDecl *D) const {
SmallString<256> Name;
raw_svector_ostream NameStream(Name);
MC->mangleCXXVTable(D, NameStream);
return getBackendMangledName(Name);
}
std::string
InstallAPIVisitor::getMangledCXXThunk(const GlobalDecl &D,
const ThunkInfo &Thunk) const {
SmallString<256> Name;
raw_svector_ostream NameStream(Name);
const auto *Method = cast<CXXMethodDecl>(D.getDecl());
if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(Method))
MC->mangleCXXDtorThunk(Dtor, D.getDtorType(), Thunk.This, NameStream);
else
MC->mangleThunk(Method, Thunk, NameStream);
return getBackendMangledName(Name);
}
std::string InstallAPIVisitor::getMangledCtorDtor(const CXXMethodDecl *D,
int Type) const {
SmallString<256> Name;
raw_svector_ostream NameStream(Name);
GlobalDecl GD;
if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(D))
GD = GlobalDecl(Ctor, CXXCtorType(Type));
else {
const auto *Dtor = cast<CXXDestructorDecl>(D);
GD = GlobalDecl(Dtor, CXXDtorType(Type));
}
MC->mangleName(GD, NameStream);
return getBackendMangledName(Name);
}
void InstallAPIVisitor::emitVTableSymbols(const CXXRecordDecl *D,
const AvailabilityInfo &Avail,
const HeaderType Access,
bool EmittedVTable) {
if (hasVTable(D)) {
EmittedVTable = true;
const CXXLinkage VTableLinkage = getVTableLinkage(D);
if (VTableLinkage == CXXLinkage::ExternalLinkage ||
VTableLinkage == CXXLinkage::WeakODRLinkage) {
const std::string Name = getMangledCXXVTableName(D);
const bool WeakDef = VTableLinkage == CXXLinkage::WeakODRLinkage;
auto [GR, FA] = Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
GlobalRecord::Kind::Variable, Avail,
D, Access, getFlags(WeakDef));
Ctx.Verifier->verify(GR, FA);
if (!D->getDescribedClassTemplate() && !D->isInvalidDecl()) {
VTableContextBase *VTable = D->getASTContext().getVTableContext();
auto AddThunk = [&](GlobalDecl GD) {
const ItaniumVTableContext::ThunkInfoVectorTy *Thunks =
VTable->getThunkInfo(GD);
if (!Thunks)
return;
for (const auto &Thunk : *Thunks) {
const std::string Name = getMangledCXXThunk(GD, Thunk);
auto [GR, FA] = Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
GlobalRecord::Kind::Function,
Avail, GD.getDecl(), Access);
Ctx.Verifier->verify(GR, FA);
}
};
for (const auto *Method : D->methods()) {
if (isa<CXXConstructorDecl>(Method) || !Method->isVirtual())
continue;
if (auto Dtor = dyn_cast<CXXDestructorDecl>(Method)) {
// Skip default destructor.
if (Dtor->isDefaulted())
continue;
AddThunk({Dtor, Dtor_Deleting});
AddThunk({Dtor, Dtor_Complete});
} else
AddThunk(Method);
}
}
}
}
if (!EmittedVTable)
return;
if (hasRTTI(D)) {
std::string Name = getMangledCXXRTTI(D);
auto [GR, FA] =
Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
GlobalRecord::Kind::Variable, Avail, D, Access);
Ctx.Verifier->verify(GR, FA);
Name = getMangledCXXRTTIName(D);
auto [NamedGR, NamedFA] =
Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
GlobalRecord::Kind::Variable, Avail, D, Access);
Ctx.Verifier->verify(NamedGR, NamedFA);
}
for (const auto &It : D->bases()) {
const CXXRecordDecl *Base =
cast<CXXRecordDecl>(It.getType()->castAs<RecordType>()->getDecl());
const auto BaseAccess = getAccessForDecl(Base);
if (!BaseAccess)
continue;
const AvailabilityInfo BaseAvail = AvailabilityInfo::createFromDecl(Base);
emitVTableSymbols(Base, BaseAvail, *BaseAccess, /*EmittedVTable=*/true);
}
}
bool InstallAPIVisitor::VisitCXXRecordDecl(const CXXRecordDecl *D) {
if (!D->isCompleteDefinition())
return true;
// Skip templated classes.
if (D->getDescribedClassTemplate() != nullptr)
return true;
// Skip partial templated classes too.
if (isa<ClassTemplatePartialSpecializationDecl>(D))
return true;
auto Access = getAccessForDecl(D);
if (!Access)
return true;
const AvailabilityInfo Avail = AvailabilityInfo::createFromDecl(D);
// Check whether to emit the vtable/rtti symbols.
if (isExported(D))
emitVTableSymbols(D, Avail, *Access);
TemplateSpecializationKind ClassSK = TSK_Undeclared;
bool KeepInlineAsWeak = false;
if (auto *Templ = dyn_cast<ClassTemplateSpecializationDecl>(D)) {
ClassSK = Templ->getTemplateSpecializationKind();
if (ClassSK == TSK_ExplicitInstantiationDeclaration)
KeepInlineAsWeak = true;
}
// Record the class methods.
for (const auto *M : D->methods()) {
// Inlined methods are usually not emitted, except when it comes from a
// specialized template.
bool WeakDef = false;
if (isInlined(M)) {
if (!KeepInlineAsWeak)
continue;
WeakDef = true;
}
if (!isExported(M))
continue;
switch (M->getTemplateSpecializationKind()) {
case TSK_Undeclared:
case TSK_ExplicitSpecialization:
break;
case TSK_ImplicitInstantiation:
continue;
case TSK_ExplicitInstantiationDeclaration:
if (ClassSK == TSK_ExplicitInstantiationDeclaration)
WeakDef = true;
break;
case TSK_ExplicitInstantiationDefinition:
WeakDef = true;
break;
}
if (!M->isUserProvided())
continue;
// Methods that are deleted are not exported.
if (M->isDeleted())
continue;
const auto Access = getAccessForDecl(M);
if (!Access)
return true;
const AvailabilityInfo Avail = AvailabilityInfo::createFromDecl(M);
if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(M)) {
// Defaulted constructors are not exported.
if (Ctor->isDefaulted())
continue;
std::string Name = getMangledCtorDtor(M, Ctor_Base);
auto [GR, FA] = Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
GlobalRecord::Kind::Function, Avail,
D, *Access, getFlags(WeakDef));
Ctx.Verifier->verify(GR, FA);
if (!D->isAbstract()) {
std::string Name = getMangledCtorDtor(M, Ctor_Complete);
auto [GR, FA] = Ctx.Slice->addGlobal(
Name, RecordLinkage::Exported, GlobalRecord::Kind::Function, Avail,
D, *Access, getFlags(WeakDef));
Ctx.Verifier->verify(GR, FA);
}
continue;
}
if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(M)) {
// Defaulted destructors are not exported.
if (Dtor->isDefaulted())
continue;
std::string Name = getMangledCtorDtor(M, Dtor_Base);
auto [GR, FA] = Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
GlobalRecord::Kind::Function, Avail,
D, *Access, getFlags(WeakDef));
Ctx.Verifier->verify(GR, FA);
Name = getMangledCtorDtor(M, Dtor_Complete);
auto [CompleteGR, CompleteFA] = Ctx.Slice->addGlobal(
Name, RecordLinkage::Exported, GlobalRecord::Kind::Function, Avail, D,
*Access, getFlags(WeakDef));
Ctx.Verifier->verify(CompleteGR, CompleteFA);
if (Dtor->isVirtual()) {
Name = getMangledCtorDtor(M, Dtor_Deleting);
auto [VirtualGR, VirtualFA] = Ctx.Slice->addGlobal(
Name, RecordLinkage::Exported, GlobalRecord::Kind::Function, Avail,
D, *Access, getFlags(WeakDef));
Ctx.Verifier->verify(VirtualGR, VirtualFA);
}
continue;
}
// Though abstract methods can map to exports, this is generally unexpected.
// Except in the case of destructors. Only ignore pure virtuals after
// checking if the member function was a destructor.
if (M->isPureVirtual())
continue;
std::string Name = getMangledName(M);
auto [GR, FA] = Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
GlobalRecord::Kind::Function, Avail, M,
*Access, getFlags(WeakDef));
Ctx.Verifier->verify(GR, FA);
}
if (auto *Templ = dyn_cast<ClassTemplateSpecializationDecl>(D)) {
if (!Templ->isExplicitInstantiationOrSpecialization())
return true;
}
using var_iter = CXXRecordDecl::specific_decl_iterator<VarDecl>;
using var_range = iterator_range<var_iter>;
for (const auto *Var : var_range(D->decls())) {
// Skip const static member variables.
// \code
// struct S {
// static const int x = 0;
// };
// \endcode
if (Var->isStaticDataMember() && Var->hasInit())
continue;
// Skip unexported var decls.
if (!isExported(Var))
continue;
const std::string Name = getMangledName(Var);
const auto Access = getAccessForDecl(Var);
if (!Access)
return true;
const AvailabilityInfo Avail = AvailabilityInfo::createFromDecl(Var);
const bool WeakDef = Var->hasAttr<WeakAttr>() || KeepInlineAsWeak;
auto [GR, FA] = Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
GlobalRecord::Kind::Variable, Avail, D,
*Access, getFlags(WeakDef));
Ctx.Verifier->verify(GR, FA);
}
return true;
}
} // namespace clang::installapi