//===- 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/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(MC->getASTContext().getCanonicalTagType(D), NameStream);

  return getBackendMangledName(Name);
}

std::string InstallAPIVisitor::getMangledCXXRTTI(const CXXRecordDecl *D) const {
  SmallString<256> Name;
  raw_svector_ostream NameStream(Name);
  MC->mangleCXXRTTI(MC->getASTContext().getCanonicalTagType(D), 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, bool ElideOverrideInfo) 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, ElideOverrideInfo,
                           NameStream);
  else
    MC->mangleThunk(Method, Thunk, ElideOverrideInfo, 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, /*ElideOverrideInfo=*/true);
            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 auto *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
